Codeforces Round #717 (Div. 2)A-B详解

题目链接:https://codeforces.com/contest/1516

A.Tit for Tat


题意:给定一个数组,然后再给定n和k。你可以执行操作如下:
给任意两个不同的元素(下标不同)一个加上1,一个减掉1
最多执行k次这样的操作,并且序列中不能出现负数。请你给出一个字典序最小的数组。

字典序:参考字符串的字典序比较。

思路
1.既然要字典序最小,那么 要让前面的数尽可能的小,那么我们就尽可能的减掉当前最前面的数,给最后面的数加1。如果到0了,就下一个数。当然,k次用完了就退出了。然后要保持没有负数。


AC代码:

#include<bits/stdc++.h>
using namespace std;
const int N=220;
int n,k;
int a[N];
int t;
int main()
{
	cin>>t;
	while(t--)
	{
		cin>>n>>k;
		for(int i=1;i<=n;i++) cin>>a[i];
		for(int i=1;i<=n;i++)
		{
			if(a[i]==0||i==n) continue;
			while(a[i]>0&&k>0)
			{
				a[i]--;
				a[n]++;k--;
			}
		}
		for(int i=1;i<=n;i++) cout<<a[i]<<" ";
		cout<<endl;
	}
	return 0;
}

B–AGAGA XOOORRR

题意:给你一个序列,你可以执行以下操作:把*相邻相邻相相邻的两个数异或(^),然后生成新的数。问你是否能通过一系列这样的操作,使得序列中满足元素都相等,且元素要满足大于等于2

关于异或

  1. (X^Y) ^Z == X^(Y ^Z)
  2. X^X=0 , X^0=X, X^X ^X=X.

思路重点:

(结论1)我们从末态出发,即最终的状态是序列都是同一个数。那么这个序列的长度要么是2,要么是3。

证明:因为,X^X=0,比如当前长度为4,序列为【X,X,X,X】,我们可以通过把后两个X ^X=0,序列就变成【X,X,0】,然后才把X ^ 0=X,于是,序列就变成了【X,X】 。奇数的情况同理。

2.当序列为偶数的那种情况,我们可以发现,如果序列可以构成相同,即最终状态为偶数个X,那么我们把这么多个X一起异或,即【X^X ^X ^X…】,那么最终一定是0,
所以当最终异或和为0的序列一定可以


讨论最终长度为奇数的情况
3,当序列为奇数的情况时,我们可以发现,序列的异或和为一个数Y(n为奇数,即n=2*k+1,k对的偶数异或都为0),所有最终为一堆0和一个数Y,如果序列可以满足,那么最终的数就是Y。

4.又因为最终需要大于等于2个数,相等所以我们只需要判断这里序列是否能够化成长度大于等于2且元素都相等的序列,即是否能找到2个及以上的Y。

实现

前缀异或 ans[i]=ans[i-1]^a[i]。


AC代码:(注释)

#include<bits/stdc++.h>
using namespace std;
int t,n;
int a[2100];
int main()
{
   
    cin>>t;
    while(t--)
    {
        cin>>n;
        for(int i=0;i<n;i++)
        {
            cin>>a[i];
        }
        int ans = 0;
        for(int i=0;i<n;i++)
        {
            ans = ans^a[i]; 
        }
        if(ans == 0)
        {
            cout<<"YES\n";continue;
        }else
        {
            int temp = 0;
            int count = 0;
            for(int i=0;i<n;i++)
            {
                temp = temp^a[i];//异或前缀 
                if(temp == ans)
                {
                    count++; 
                    temp = 0;//满足的时候让他归0,可以理解把奇数凑成偶数,或者当前区间满足,找下个区间的时候从初始态开始异或。 
                }
            }//cnt就是记录最后剩下的数是不是大于2,已知最后一个数是Y,那么最少在前面再找一个Y即可。 
            if(temp == 0 && count>1 ) cout<<"YES\n";
            else cout<<"NO\n";
        }
    }
    

    return 0;
}

解法二

同样的,既然要使最终元素都相同。
那么,一定是若干个区间生成相同的数,组成的序列。
例如一个序列为【A,B,C,D,E,F】
那么在异或计算下可以是【A,B】=X,【C,D,E】=X,【F】=X。
再根据上面所讲,

要么分成2个区间,要么分成3个区间

异或前缀的运用

==类似于前缀和求区间【L,R】的和,区间【L,R】的异或和也可以通过
f[R]^f[L-1]。求得。

区间[L,R]异或和为 f[R]^f[L-1]

所以,我们把前缀异或和数组处理好后,直接枚举区间不就行了。

枚举2个区间1个for,枚举3个区间2个for,我这里一起处理了

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e3+10;
map<ll,ll> mp;
ll a[N];
ll ans[N];
ll ans2[N];
int t;
int n;
void solve()
{
	if(n==1)
	{
		cout<<"NO"<<endl;return;
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=i;j<=n;j++)
		{
			if(i==j&&ans[i]==(ans[n]^ans[i])) // [1,i] 和[i+1,n] 这两个区间。 
		{
			cout<<"YES"<<endl;return;
		}
		if(i!=j&&ans[i]==(ans[n]^ans[j])&&(ans[i]==(ans[i]^ans[j]))) //[1,i] 和 [i+1,j] 和 [j+1,n] 3个区间 
			{
				cout<<"YES"<<endl;
				return ;
			 }  
		}
	}
	
	cout<<"NO"<<endl;
	return ;
	 
}
int main()
{
	cin>>t;
	while(t--)
	{
		scanf("%d",&n);
		ll res=0;
		for(int i=1;i<=n;i++)
		{
		scanf("%lld",&a[i]);
		res=res*a[i]; 
		ans[i] = ans[i-1] ^ a[i];
		}
		solve();
	
	}
	return 0;
}

C代码

AC代码

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int f[N];
int a[N];
int t,n;
int sum;
bool check()
{
	f[0]=1;
	for(int i=1;i<=n;i++)
	{
		for(int j=N;j>=0;j--)
		{
			f[j+a[i]]+=f[j];
		}
	}
//	printf("f[sum/2]===%d \n",f[sum/2]);
	if(f[sum/2]) return false;
	return true;
}
int main()
{
	
		cin>>n;
		//int sum=0;
		for(int i=1;i<=n;i++)
		{
			cin>>a[i];
		}
		int k=a[1];
		for(int i=2;i<=n;i++)
		{
			k=__gcd(k,a[i]);
		}
		for(int i=1;i<=n;i++)
		{
			a[i]/=k;sum+=a[i];
		}
	//	printf("k===%d \n",k);
		if(sum%2==1)
		{
			cout<<"0"<<endl;return 0;
		}
		else
		{
			int st;//下标
		//	printf("st===%d \n",st);
			for(int i=1;i<=n;i++)
			{
				if(a[i]%2==1)
				{
					st=i;
				}
			}
		//	printf("st===%d \n",st);
	//		printf("chek=== %d\n",check());
			if(check()==1) 
			{
				cout<<"0"<<endl;
			}
			else
			{
				cout<<"1"<<endl;
				cout<<st<<endl;
			}
		}
		return 0;
	
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值