Atcoder beginner contest(ABC) 165游记

值得纪念的一场比赛,所以写个游记

游记

8:00有一场比赛,早早地做好了准备,心中燃放着一股怒火。但是,不知道是网站崩溃了,还是家里的网络崩溃了,本蒟蒻迟迟进不去Atcoder,而班级里的好多人都成功进去了并告诉我8:10才开始。

结果从8:00一直等待响应到8:08,才加载出比赛的页面。在这个期间,我崩溃了两次

比赛一开始就打开A题。刚开始以为是一道数学题,可在看完数据范围之后一顿暴力枚举轻松切掉。

但是毕竟A题总是很水,所以我根本没有增强信心。

接着打开B题,发现是一道模拟题。于是,我让100每次乘1.01,直到到达 X X X的时候停止,并记录下次数。

惊人地发现前两题花了15分钟,到底是我太菜还是网络太菜?

接着打开C题,发现难度飙升——dp? 贪心?两次惨痛的WA告诉了我这都不是。

之所以我没有用正解暴力,是因为我没有看到一个重要东西: 1 ≤ a 1 ≤ a 2 ≤ a 3 ≤ … … a n ≤ m 1≤a_1≤a_2≤a_3≤……a_n≤m 1a1a2a3anm

所以枚举 a a a数组并不是全排列。那个时候脑子瓦特的我以为这是全排列,时间复杂度 O ( n ! q ) O(n!q) O(n!q),会超时!

然后我打了一个全排列的暴力,连样例都过不了。于是,我debug了好久好久,就是没看到那一句话,然后没有崩溃而是自闭了。

但是当我想到一年前一场AT比赛,我过了A, B, D, E题时,我才振作起来打开D和E题。

打开D题一看,什么东西啊?神仙题吗?

那时的本蒟蒻认为,本题中的 x x x a , b , c a,b,c a,b,c没有任何关系,所以可能是一个二分。

结果往二分方向苦想无果,又看了一眼数据范围—— a ≤ 1 0 6 a≤10^6 a106

所以说,本蒟蒻傻傻的以为这题是要弄一个 O ( a ) O(a) O(a)时间复杂度的算法。而这种算法一定就是枚举 x x x,其中 x ≤ m i n ( a , n ) x≤min(a,n) xmin(a,n),这个就是正解啦!

于是我要交了,等待我的仍然是WA。
接着,我怀着焦急的心理打开QQ,发现班级里一位神仙已经秒切了A,B,C,D题。

心态真得炸了!心态真得炸了!心态真得炸了!

那怎么办?拼啊!

我关掉QQ,闭上眼睛,然后盯着第三题的题面

…………

我貌似看到了一行字: 1 ≤ a 1 ≤ a 2 ≤ a 3 ≤ … … a n ≤ m 1≤a_1≤a_2≤a_3≤……a_n≤m 1a1a2a3anm

要个毛全排列啊,TM直接组合计数不久TM可以了吗,我TM怎么这么笨啊!

然后我以为会切掉这题,不料命运多舛,我又WA了。但是没事,我发现组合计数写错了,改一改加个break即可。

距离比赛结束78分钟,我终于AC了前三题。

然后到D题。

有一个很明显的东西,就是含有 x x x的分数的分母都是 b b b

f l o o r ( a × x / b ) − a × f l o o r ( x / b ) floor(a×x/b)-a×floor(x/b) floor(a×x/b)a×floor(x/b)

找规律无果。接着我想到 a ≤ 1 0 6 a≤10^6 a106并不是为了让你搞有关 O ( a ) O(a) O(a)的算法,而是不让答案溢出。

通过观察,我猛然发现——贪心?!?!?!

x = b − 1 x=b-1 x=b1的时候,原来的式子总是最大的。

但是有 n n n的限制,所以这里 x = m i n ( n , b − 1 ) x=min(n,b-1) x=min(n,b1)

这个推理过程虽然不严谨,但是看到比赛将在2分钟后结束的时候,我果断地开始打代码。

键盘微凉 鼠标微凉

指尖流淌 代码千行

距离比赛结束还有32秒~

拉到最下面,提交!

2/32
9/32
18/32
26/32
31/32
AC!

克制住掀桌而起的疯狂心理,我摊在了地上。

由于当天晚上太累了,早早就睡了(颓废);所以并没有看F题,只是看了看E题的题面而已。

很遗憾,这场比赛没有发挥好,排名连前2000都没进。但是我感觉吸收到了太多东西,特别是Rating涨的37,是我前进的动力。

5.3还有一场比赛,加油吧!

题解

T1

直接暴力枚举即可。建议不要往数学的方面去想。

#include 
#define int long long
using namespace std;

int a,l,r;

signed main()
{
	cin>>a>>l>>r;
	for (int i=l;i<=r;i++)
	{
		if (i%a==0)  return cout<<"OK"<<endl,0;
	}
	cout<<"NG"<<endl;
	
	return 0;
}

T2

注意,若将一个整形量乘上一个float或double(1.01)量的值存到int里面去,那么它会自动保留整数部分。

由于答案最大为3760,所以直接模拟就可以了。

#include 
#define int long long
using namespace std;

int n=100,x;

signed main()
{
	cin>>x;
	for (int i=1;i<=3760;i++)//while懒得写了
	{
		n=n*1.01;
		if (n>=x)  return cout<<i<<endl,0;
	}
}

T3

这一次的T3考验的是代码能力。

我们需要使用求组合的方式来得到数组 A A A的各个元素的值。如果您对组合计数不太熟悉,建议看下下面这部分。

组合计数

①所有数都设为1(1 1 1 1);

②从右边往左边看,如果某个位置的数小于m,那么就把它加1并再从头往前看;

③如果第0个位置不再是0了,就1至 n n n的所有的位置都变成 m m m了;此时跳出循环。

for (int i=n;i>=0;i--)
{
	if (now[i]<m)//now数组表示上面的A数组
	{
		now[i]++;
		for (int j=i+1;j<=n;j++)  now[j]=now[j-1];
		break;
	}
}

例如: n = 3 , m = 2 n=3,m=2 n=3,m=2

1 1 1(第一步)
1 1 2(第三个位置不是m,加一)
1 2 2(第二个位置不是m,加一)
2 2 2(第一个位置不是m,加一)
然后 a 0 a_0 a0就变成1了,因此跳出循环。

#include 
#define int long long
using namespace std;

int n,m,q,ans=-1e9-7;
int now[100005];

struct node
{
	int a,b,c,d;
}que[100005];

signed main()
{
	cin>>n>>m>>q;
	for (int i=1;i<=q;i++)  cin>>que[i].a>>que[i].b>>que[i].c>>que[i].d;
	for (int i=1;i<=n;i++)  now[i]=1;
	
	while (now[0]==0)
	{
		int sumv=0;
		for (int i=1;i<=q;i++)
		{
			if (now[que[i].b]-now[que[i].a]==que[i].c)  sumv+=que[i].d;
		}
		ans=max(ans,sumv);
		
		for (int i=n;i>=0;i--)
		{
			if (now[i]<m)
			{
				 now[i]++;
				 for (int j=i+1;j<=n;j++)  now[j]=now[j-1];
				 break;
			}
		}
	}
	cout<<ans<<endl;
	
	return 0;
}

T4

一道不错的数学贪心题。

首先,有一个很明显的东西,就是含有 x x x的分数的分母都是 b b b。最佳的 x x x可能由 n n n有关(不能超出上界),那是否与 b b b有关呢》

通过观察,发现当 x = b − 1 x=b-1 x=b1的时候,原来的式子总是最大的。

但是有 n n n的限制,所以这里 x = m i n ( n , b − 1 ) x=min(n,b-1) x=min(n,b1),才能让式子最大。

求出 x x x然后带进去就行啦。不要被 a ≤ 1 0 6 a≤10^6 a106给误导哦,这仅仅是为了防止下标溢出。

时间复杂度: O(1)

#include 
#define int long long
using namespace std;

int a,b,n,N;

signed main()
{
	cin>>a>>b>>N;
	n=min(N,b-1);
	cout<<((a*n)/b)-a*(n/b)<<endl;
	
	return 0;
}

T5

一道神仙 构造题,反正比赛的时候WA了无数次,就是没有想到正解。

即,分组,编号为 1 1 1 m + 1 m+1 m+1算一组, m + 2 m+2 m+2 n n n算另外一组。然后第一组中 1 1 1 m + 1 m+1 m+1, 2 2 2 m m m 3 3 3 m − 1 m-1 m1……第二组同理,即 m + 2 m+2 m+2 n n n m + 3 m+3 m+3 n − 1 n-1 n1……

注意当已经凑足 m m m场比赛的时候立即停止,否则同时在第一组中和第二组中搞一场比赛( 1 1 1 m + 1 m+1 m+1 m + 2 m+2 m+2 n n n),这样的正确性是不言而喻 的。

时间复杂度: O ( m ) O(m) O(m)
我太难了……

#include 
#define int long long
using namespace std;

int n,m,q,ans=-1e9-7;
int now[100005];

struct node
{
	int a,b,c,d;
}que[100005];

signed main()
{
	cin>>n>>m>>q;
	for (int i=1;i<=q;i++)  cin>>que[i].a>>que[i].b>>que[i].c>>que[i].d;
	for (int i=1;i<=n;i++)  now[i]=1;
	
	while (now[0]==0)
	{
		int sumv=0;
		for (int i=1;i<=q;i++)
		{
			if (now[que[i].b]-now[que[i].a]==que[i].c)  sumv+=que[i].d;
		}
		ans=max(ans,sumv);
		
		for (int i=n;i>=0;i--)
		{
			if (now[i]<m)
			{
				 now[i]++;
				 for (int j=i+1;j<=n;j++)  now[j]=now[j-1];
				 break;
			}
		}
	}
	cout<<ans<<endl;
	
	return 0;
}

T6

请教一下各位大佬,怎么在优先队列中删除一个元素?

如果我知道了,这篇题解就可以写了……

你可能感兴趣的:(比赛题解,数学)