Codeforces Round 920 (Div. 3)(A~F)

河南下雪了,还挺好的,突然想到在师大这几年还是没有下过雪,回来就直接暴雪,可能这就是天意吧,这场比赛E题没有想到两人位于同一Y轴应该是bob必赢的没有写出来,挺遗憾的,但是也没有什么办法,俯冲1300。

A. Square

我们可以想到,对于正方形的任何一个顶点,他到其他两个顶点的距离都等于边长,到另外一个顶点的距离为根号二倍的边长,但是这里可以不用考虑,以第一个顶点计算到其他三个顶点的距离,出现次数为2的数字即为边长的平方。

void solve() {
	vector>v(4);
	for(int i = 0 ;i < 4 ;i ++)
	{
		cin >> v[i].first >> v[i].second;
	}
	mapma;
	for(int i = 1 ;i < 4 ;i ++)
	{
		ma[(v[i].first - v[0].first) *(v[i].first - v[0].first ) + 
      (v[i].second - v[0].second) *(v[i].second - v[0].second)]++;
	 }
	 for(auto i : ma)
	 {
	 	if(i.second == 2)
	 	cout << i.first << endl;
	  } 
}

B. Arranging Cats

Codeforces Round 920 (Div. 3)(A~F)_第1张图片

对于b题,我们可以发现想要让字符串变为指定的字符串,分为两种情况,变化前为0,变化后为1,变化前为1,变化后为0。即使面对1和0的数字不同,其实也只需要一个额外的操作去使得一个位置变化,但是我们可以发现0 1 变为1 0这个过程其实是可以只进行一次就可以将两个位置上0 1 和 1 0 变为00 11,因此我们只需要分别计算出两种情况的值,最后取最大值即可。

void solve() {
	int n;
	cin >> n;
	string s1,s2;
	cin >> s1 >> s2;
	int l = 0 ,r = 0;
	for(int i = 0 ;i < n ;i ++)
	{
		if(s1[i] == '1' && s2[i] == '0')
		l++;
		if(s2[i] == '1' && s1[i] == '0')
		r++;
	}
	cout << max(l,r)<< endl;
}

C. Sending Messages

Codeforces Round 920 (Div. 3)(A~F)_第2张图片

这个c题,我不好评价,其实就一个状态转移,但是读不懂题意,赛时卡了好久,在0电量的时候就没有办法再发送信息了,怪自己没注意到这点吧。

对于这个题,我们可以注意到,其实当转移到不同时刻时,有两种情况,一是关机再开机,这个过程消耗b电量,二是不关机,但是每天会消耗a * 天数电量,因此我们可以每一次记录上一次需要发短信的位置,每一次计算到当前位置需要的最少电量,若是最少电量大于等于电量,就没办法发送最后一条短信,若是小于的话,则说明可以正常发送所有短信。

void solve() {
	int n,f,a,b;
	cin >> n >> f >> a >> b;
	vectorv(n);
	for(int i = 0 ;i < n ;i ++)
	cin >> v[i];
	int sum = 0 ,ans = 0;
	for(int i = 0 ;i < n ;i ++)
	{
		ans = v[i] - ans;
		sum += min(a * ans , b);
		ans = v[i];
	}
	if(sum >= f)
	cout << "NO" << endl;
	else 
	cout << "YES" << endl;
}

D. Very Different Array

Codeforces Round 920 (Div. 3)(A~F)_第3张图片

d题是一个比较有意思的题目,为了使权值最大,我们可以想到,分为取最大和最小值两种情况,若是 1 2  , 5 6,我们会发现,无论1选择5还是6,其实对结果都没有影响,若是5 6 , 1 2 我们怎么选择仍然没有影响,因此我们可以总结出只有1 6 , 2 3 这种会有影响,会选择一个比他大或者小的数字,因此我们去看样例一

Codeforces Round 920 (Div. 3)(A~F)_第4张图片

先将数字从小到大排序,此时1有1 和 7 ,分别从最大和最小值里面选择,若是1选择1,对整个序列来讲,自然是 3 3 2 1 这种情况为最优解,假设我们选择了7 , 对于后面的2 4 6 ,我们要选择2和5对应还是整个序列选择1 2 3来获得最优解,以这个思想进行比较,我们会发现每次对于a[i],选择的对象就是a[n-i-1]或者a[m-i-1],若是有一个时候a[n-i-1]为最优解,则说明后面的都选择a[n-i-1]这种情况为最优解。以下是对1进行分析

Codeforces Round 920 (Div. 3)(A~F)_第5张图片

代码如下:

void solve() {
	int n,m;
	cin >> n >> m;
	vectora(n),b(m);
	for(auto &i : a)
	cin >> i;
	for(auto &i : b)
	cin >> i;
	sort(a.begin(),a.end());
	sort(b.begin(),b.end());
	int flag = 0 , sum = 0;
	for(int i = 0 ;i < n ;i ++)
	{
		if(abs(a[i] - b[n - i - 1]) <= abs(a[i] - b[m - i - 1]) && flag == 0)
		sum += abs(b[m - i - 1] - a[i]);
		else
		{
			flag = 1;
			sum += abs(b[n-i-1]-a[i]);
		}	
	 } 
	 cout << sum << endl;
}

E. Eat the Chip

E题其实赛时能感觉出来我就是少考虑了一种情况,但是分析了好久始终没有分析到,也没有办法,挺无奈的吧。

Codeforces Round 920 (Div. 3)(A~F)_第6张图片

对于这道题,我们会想到,若是黑白间差距为2个单元格,这个时候因为无论如何他们在x轴上都是互相靠近,所以这个时候一定是平局或者白棋赢,若是奇数,则是黑棋赢或者平局。但是要考虑以最优情况来实现,我们可以想到,若是黑棋或者白棋明白自己要输,那么选择逃跑来获得平局一定是最优解,而若是两个棋子到了同一个y上,则说明此时不可能会产生平局,一个棋子只需要跟着另一个棋子运动即可,因此当一个棋子逃到边界处无路可退时则说明会产生赢家,所以对要输的棋子来讲,他只能选择朝着距离赢家更远的位置逃跑,在两个人x轴接近时若是不会跑到边界,则可以平局,否则则必输。但是同时我们要考虑白棋的先手优势,也就是说当y轴距离小于等于1时,此时白棋一定能追上黑棋,若是黑棋赢的话,此时x轴距离应该是相等,黑棋一定能追上白棋。同时我们也需要考虑若是两个棋子间x轴的距离小于y轴则一定互相触碰不到,一定是平局。

void solve() {
	int l,r,x1,y1,x2,y2;
	cin >> l >> r >> x1 >> y1 >> x2 >> y2;
	if(x2-x1 < abs(y1-y2))
	{
		cout << "Draw" << endl;
	}
	else if(abs(x1 - x2 ) % 2 == 1)
	{
		if(y1 < y2)
		{
			if( 2 * (r - y1 ) - 1 > x2 - x1 && abs(y1-y2) > 1)
			cout << "Draw" << endl;
			else
			cout <<"Alice" << endl;
		}
		else
		{
	    	if(2 * (y1 - 1) - 1> x2 -  x1 && abs(y1-y2) > 1)
			cout << "Draw" << endl;
			else
			cout <<"Alice" << endl;
		}
	}
	else
	{
		if(y1>y2)
		{
			if(2 * (r - y2 )  > x2 - x1 && y1 != y2)
			cout << "Draw" << endl;
			else
			cout <<"Bob" << endl;
		}
		else
		{
	    	if(2 * (y2 - 1 )> x2 - x1 && y1 != y2)
			cout << "Draw" << endl;
			else
			cout <<"Bob" << endl;
		}
	}
}

F. Sum of Progression

对于这道题,我们可以想到,查询会出现重复情况导致我们需要多次访问同一下标会增加访问次数,从而使得时间超过限制,这个时候我们可以采取根号分治来做这道题,不明白这个思想的可以看下述链接:根号分治学习笔记 - Eason2009 的博客 - 洛谷博客 (luogu.com.cn)

通过分治,我们对小范围内出现频率很高的数字来进行分治,采取前缀和的思想去做,对于较大的数则直接进行访问,我们将查询的步长d作为标识符,对每个步长相等且小于我们分治的范围的值进行同时处理,减少访问次数,在这里处理时,要特别注意下标处理,因为题目要求as+as+d⋅2+⋯+as+d⋅(k−1)⋅k,因此我们可以想到,需要使用两个前缀和,一个前缀和用来处理题目要求的指定的结果,即从1~n的前缀和乘以权值,另外的直接是前缀和不乘以权值,当我们在计算时,只需要将对应的结果减去起始位置的值,随后再乘以初坐标作为对应的份数和即可。这道题在计算时需要特别重视对下标的处理,很容易越界(我就卡边界卡了半个小时)。

const int S = 200;

void solve() {
	int n,q;
	cin >> n >> q;
	vectora(n);
	for(int i = 0 ;i < n ;i ++)
	cin >> a[i];
	vector>>b(S + 1);
	vectorv(q);
	for(int i = 0 ;i < q ;i ++)
	{
		int s,d,k;
		cin >> s >> d >> k;
		s--;
		if(d > S)
		{
			int sum = 0;
			for(int j = 1 ;j <= k ;j ++)
			{
				sum += a[s + (j - 1) * d] * j;
			}
			v[i] = sum;
		}
		else
		{
			b[d].push_back({s,k,i});
		}
	}
	for(int i = 1 ;i <= S ;i ++)
	{
		if(b[i].empty()) continue;
		vectors1(n),s2(n);
		for(int j = 0 ;j < n ;j ++)
		{
			if(j >= i)
			{
				s1[j] = s1[j - i];
				s2[j] = s2[j - i];
			}
				s1[j] += a[j];
				s2[j] += (j/i) *a[j];
		}
			for(auto [st,k,id] : b[i])
		{
			int ed = st + (k - 1) * i;
			int t = st/ i;
			if(t == 0)
			v[id] = s2[ed] + s1[ed];
			else
			v[id] = s2[ed] - s2[st - i] -(t - 1) * (s1[ed] - s1[st-i]);
		}
	}
	for(auto i : v)
	cout << i << " ";
	cout << endl;
}

你可能感兴趣的:(算法,c++)