洛谷P8453 美元巨大题解

比赛的时候乍眼一看这么多 2 2 2 直接不想写了,结果在重现赛重现看的时候感觉没有那么难了,就勇敢去贪就好了。

贪心思路

首先,我们很容易得到,无论前面有重复出现了多少次该位的数,不管它前面是被异或了还是或了,我们只需要在该位最后一次出现的时候用一次或运算即可保留其对答案的贡献。

其次,比如对于二进制数 ( 11011011 ) 2 (11011011)_2 (11011011)2,我们就算将 7 7 7 位全部保留,也比只保留第 8 8 8 位要小,因此尽可能保留最大的一位才能使结果最优。

最后,对于重复出现了奇数次的数位,显然我们用什么运算符都不会对答案造成影响,因此我们只需要保留偶数次位就行了。

对于多余的或,因为其无论放在哪都不会对结果造成干扰,所以可以直接全部放在最后面。

总结:在偶数次出现的大数位用或运算将其保留。

接下来是带注释的代码:

Code

#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int x=0,f=1;char c=getchar();
	for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
	for(;isdigit(c);c=getchar())x=(x<<3)+(x<<1)+(c^48);
	return x*f;
}
int S,T,n,x,y,xx,yy;
int a[25004],ans[65540],g[65540],maxa,appear[65540];
//a数组存数据,ans存最优答案,g存该位出现的次数,maxa是最大数位,appear表示遍历过程中该位出现了几次,用于判断是不是最后一位 
bool fh[25004],baoliu[65540];
//fh表示该位的符号(1亦或,0或),baoliu表示此位要不要保留 
int main(){
	S=read(),T=read();
	while(T--){
		memset(ans,0,sizeof(ans));
		memset(g,0,sizeof(g));
		memset(appear,0,sizeof(appear));
		memset(baoliu,0,sizeof(baoliu));
		maxa=0;
		memset(fh,0,sizeof(fh));
		n=read(),x=read(),y=read();
		xx=x,yy=y;
		for(int i=1;i<=n;i++){
			a[i]=read();
			if(a[i]>maxa)maxa=a[i];
			g[a[i]]++;
		}
		for(int i=maxa+2;i>=0;i--){
			if(!yy)break;//如果没有或了就只能结束了 
			if(g[i]&&g[i]%2==0){//如果这一位存在并且出现了偶数次,就消耗一个或去保留 
				yy--;
				baoliu[i]=1;
			}
		}
		ans[a[1]]=1;
		appear[a[1]]=1;//先把1保存了 
		for(int i=2;i<=n;i++){
			appear[a[i]]++; 
			if(baoliu[a[i]]&&appear[a[i]]==g[a[i]]){
				//如果该位要保留并且是最后一次出现就把符号标记为或 
				fh[i-1]=0;
				ans[a[i]]=1;
				continue;
			}
			ans[a[i]]^=1;//0->1,1->0
			fh[i-1]=1;//先标记为亦或位 
		}
		bool flag=0;
		for(int i=maxa+2;i>=0;--i){
			if(!flag&&ans[i]){//去前导0 
				flag=1;
			}
			if(flag)printf("%d",ans[i]);
		}
		if(!flag)cout<<0;//防止全0情况 
		cout<<endl;
		int ox=0;
		for(int i=1;i<n;i++){
			if(fh[i]&&ox<x){//亦或如果不够就把或全输出了,对答案没有影响 
				cout<<"^";
				ox++;
			}
			else{
				cout<<"|";
			}
		}
		cout<<endl;
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值