Codeforces Round 896 (Div. 2)补题

发布时间:2024年01月18日

Make It Zero(Problem - A - Codeforces

题目大意:有一个数组a[],我们每次可以选定一个区间,求区间内的异或和s,然后将区间中的数全部改成s,最后要使所有元素都变成0,问需要操作多少次,每次选的区间是是什么。(不要求最优解)

思路:我们可以发现,如果选定一个偶数长度的区间,这个区间的异或和如果不为0,那么再选它一次就够了基于这个性质来求解,一定要记住偶数个相同的非0数的异或值才为0,奇数个是不可以的。而且这个题没说最小,所以一个区间即使为0了同样可以再选它。

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int n;
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
		{
			int x;
			cin>>x;
		}
		if(n%2==0)
		{
			printf("2\n");
			printf("1 %d\n",n);
			printf("1 %d\n",n);
		}
		else
		{
			printf("4\n");
			printf("1 %d\n",n-1);
			printf("1 %d\n",n-1);
			printf("2 %d\n",n);
			printf("2 %d\n",n);
		}
	}
}

2D Traveling(Problem - B - Codeforces

题目大意:有n个城市,其中k个城市是主要城市,主要城市之间通航免费,其余城市之间通行的费用是两个城市坐标的曼哈顿距离,现在要从城市a到城市b,问最少花费。

思路:如果要经过主要城市那肯定是两者都到离它最近的主要城市去,然后免费飞一次,如果不转直接飞就算一下曼哈顿距离,比较一下谁更小即可。另外两个城市之间曼哈顿距离的最大值是4e9,最小值初始化的时候要注意一下。

#include<bits/stdc++.h>
using namespace std;
#define int long long
signed main()
{
	int t;
	scanf("%lld",&t);
	while(t--)
	{
		int n,k,a,b;
		scanf("%lld%lld%lld%lld",&n,&k,&a,&b);
		vector<pair<int,int>>p;
		int x1,y1,x2,y2;
		for(int i=1;i<=n;i++)
		{
			int x,y;
			scanf("%lld%lld",&x,&y);
			if(i<=k) p.push_back({x,y});
			if(i==a) x1=x,y1=y;
			if(i==b) x2=x,y2=y;
		}
		int mi1=4e9,mi2=4e9;
		for(int i=0;i<p.size();i++)
		{
			int x=p[i].first,y=p[i].second;
			int d1=abs(x1-x)+abs(y1-y);
			int d2=abs(x2-x)+abs(y2-y);
			mi1=min(mi1,d1);
			mi2=min(mi2,d2);
		}
		int ans=abs(x1-x2)+abs(y1-y2);
		ans = min(ans,mi1+mi2);
		cout<<ans<<endl;
	}
}

Fill in the Matrix(Problem - C - Codeforces

题目大意:现有一个为空的n行m列的矩阵,要求往矩阵中填数,每一行都是一个排列,然后求每一列的mex,然后再求所有列mex的mex,最后需要输出矩阵和最终mex的值。

思路:这道题求mex的过程实际很简单,很容易发现,m>n的时候,mex的值就取决于行数,有多少行就有多少个0,那么我们就可以将这些0,尽可能地分散,那么就可以产生0-n这么多个mex值,最后总的mex就是n+1,如果m<=n,那么我们可以将m个数尽可能地分散,那么就前m-1行尽可能分散,后面地每一列都填一样的数,而且与第m-1行相同,那么m列就可以产生0-m-1个mex值,最后的结果就是m。当然有一个特例,m=1的时候,我们只有数字0可以填,那么这一列的mex是1,最后结果的mex是0。然后关于尽可能打散的问题,我们只要按照锯齿形来填相同的数即可,那么锯齿形其实横纵坐标是有规律的。如下图就是一种赋值方式:

当然还有别的赋值方式,只要保证全部打散即可,为了防止每一列所有的值都有,我们在适当的时候将每一列后面所有值全部统一。

但是由于这道题的数据范围比较特殊,建数组不太好建,所以最好找到规律逐行输出。

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int n,m;
		scanf("%d%d",&n,&m);
		if(m==1) printf("0\n");
		else if(n<m) printf("%d\n",n+1);
		else printf("%d\n",m);
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=m;j++)
			{
				cout<<(j+min(i,m-1)-1)%m<<" ";
			}
			cout<<endl;
		}
	}
}

Candy Party (Easy Version)(Problem - D1 - Codeforces

题目大意:现在有n个人,每人手里有若干个糖果,每人只能也必须进行一次操作:就是挑选一个人,送出2^x个糖果,当然这个2^x需要小于它手里送出前的糖果数(因为它可以先收再送),要求,每个人必须且只能送出一次糖果,同时也必须且只能收到一次糖果,最后所有人的糖果数量要相同,问是否可以实现。

思路:首先这里的2^x就很明显提示我们要从二进制的角度考虑,然后我们来思考,每个人既要送出又要获得,那么最后净变化是多少。很显然就是从目前的数量到它们的平均数,如果平均数非正数,那么显然不能成立。然后我们算出每个人的净变化了就要从这里入手,假设净变化的绝对值是d,那么它送出2^x个,获得2^y,个它们之间差的绝对值要等于净变化的绝对值。我们从二进制的角度来看这个减法,

1000000

0000010

相减:
0111110

所以如果可以通过减法得到,那么就找出最高位1的位置c,和最低位1的位置l

2^(c+1)-2^l就可得到。

那么我们就对差值d进行c和l的查找,如果找的的值相减之后不等于d,就说明d没办法得到,是无效值,否则就记录这两个值,我们用一个数组来记录需要给出的值,一个数组记录需要获得的值,两个数组排序后相等就说明可以成立,否则就不可以。

#include<bits/stdc++.h>
using namespace std;
#define int long long
int a[200010];
int b[70];
signed main()
{
	b[1]=1;
	for(int i=2;i<=60;i++) b[i]=b[i-1]*2;
	int t;
	scanf("%lld",&t);
	while(t--)
	{
		int n;
		scanf("%lld",&n);
		int sum=0;
		for(int i=0;i<n;i++)
		{
			int x;

			scanf("%lld",&x);
			a[i]=x;
			sum += x;
		}
		if(sum%n==0) 
		{
			int avg=sum/n;
			vector<int>g,s;
			int flag=1;
			for(int i=0;i<n;i++)
			{
				int d=abs(a[i]-avg);
				int tmp=a[i]-avg;
				int c=0,l=-1;
				while(d)
				{
					c++;
					if(l==-1&&d%2) l=c;
					d/=2;
				}//中间有0的情况没判断
				if(b[c]==abs(tmp))
				{
					if(tmp>0)//多给少收
					{
						g.push_back(c);
						s.push_back(0);
					}
					else if(tmp<0)
					{
						g.push_back(0);
						s.push_back(c);
					}
				}
				else
				{
					if(tmp&&b[c+1]-b[l]!=abs(tmp)) 
					{
						flag=0;
						break;
					}
					if(tmp>0)//多给少收
					{
						g.push_back(c+1);
						s.push_back(l);
					}
					else if(tmp<0)
					{
						g.push_back(l);
						s.push_back(c+1);
					}
				}
			}
			if(flag==0) printf("NO\n");
			else
			{
				sort(g.begin(),g.end());
				sort(s.begin(),s.end());
				if(g==s) printf("YES\n");
				else printf("NO\n");
			}
		}
		else printf("NO\n");
	}
}

?显然这么写还是略显麻烦,首先那个预处理实际上可以用位运算替代,位运算的时间复杂度是O(1)所以我们甚至不用这么写,直接写个嵌套循环去找c+1和l,如果能找到就记录,如果找不到直接判否即可。另外由于两个数组相等的性质,我们可以直接对对应位进行标识,如下

for(int k=0;k<n;k++)
{
	for(int i=31;i>=0;i--)
	{
		for(int j=31;j>=0;j--)
		{
			if(a[k]+(1<<i)-(1<<j)==avg)
			{
				d[i]++;
				d[j]--;
			}
		}
	}
}
int flag=1;
for(int i=31;i>=0;i--)
{
	if(d[i])
	{
		flag=0;
		break;
	}
}
if(flag) printf("YES\n");
else printf("NO\n");

这样简单很多。

Candy Party (Hard Version)(https://codeforces.com/contest/1869/problem/D2

题目大意:与上面大差不差,就是现在每个人最多只能给出一次,最多只能获得一次,其余的条件没有区别。

思路:这里其实真的区别不大,只不过那些差值恰好是2的幂的数多了一种选择而已。所以我们可以首先将它们全部记成不拆解,然后当不够的时候再拆解它们来补充,并不用关心每个数具体拆不拆。然后问题就解决了,但是代码部分有点麻烦。

#include<bits/stdc++.h>
using namespace std;
#define int long long
int p1[100],p2[100],b[100],a[200010];
signed main()
{
	int t;
	scanf("%lld",&t);
	
	while(t--)
	{
		int n;
		int sum=0;
		scanf("%lld",&n);
		for(int i=0;i<=34;i++) p1[i]=0,p2[i]=0,b[i]=0;
		for(int i=1;i<=n;i++)
		{
			scanf("%lld",&a[i]);
			sum += a[i];
		}
		if(sum%n) 
		{
			printf("NO\n");
			continue;
		}
		sum /= n;
		int flag=1;
		for(int i=1;i<=n;i++)
		{
			if(a[i]==sum) continue;
			int st=0;
			for(int j=31;j>=0;j--)
			{
				if(a[i]-(1<<j)==sum)//给出
				{
					p1[j]++;
					st=1;
					break;
				}
				if(a[i]+(1<<j)==sum)//获得
				{
					p2[j]++;
					st=1;
					break;
				}
			}
			if(st) continue;
			for(int j=31;j>=0;j--)
			{
				for(int k=31;k>=0;k--)
				{
					if(a[i]+(1<<j)-(1<<k)==sum)//-是获得,+是给出,因为给出的可以给后面
					{
						b[j]--;
						b[k]++;
						st=1;
						break;
					}
				}
			}
			if(!st) 
			{
				flag=0;
				break;
			}
		}
		if(!flag)
		{
			printf("NO\n");
			continue;
		}
		int st=1;
		for(int i=31;i>=0;i--)
		{
			b[i] += p1[i]-p2[i];//剩余可以给出的。
			if(b[i]==0) continue;
			if(b[i]>0)//可以给出的有剩余,能补给下一位
			{
				//实际就是将一部分需要获得2^(i-1)的拆成获得2^i,给出2^(i-1)
				//那就是直接被用作获得2^(i-1)的部分变少了,减少的个数刚好是b[i]
				//同时它给出一部分2^[i-1],那么这个相当于变多了
				p2[i-1] -=  b[i];
				b[i-1] += b[i];
				if(p2[i-1]<0)//如果能被拿来用的不够
				{
					st=0;
					break;
				}
			}
			else//b[i]为负,则需要活得,那么就要拆一部分需要给出的2^(i-1)
			{
				//将一部分需要给出的2^(i-1)变成获得2^(i-1),给出2^i
				//那么直接用于给出的2^(i-1)变少,
				//需求的2^(i-1)变多,b中的2^(i-1)应该变少
				p1[i-1] -= -b[i];
				b[i-1] -= -b[i];
				if(p1[i-1]<0)
				{
					st=0;
					break;
				}
			}
		}
		if(st) printf("YES\n");
		else printf("NO\n");
	}
}

文章来源:https://blog.csdn.net/m0_74120503/article/details/135665054
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。