比赛的时候乍眼一看这么多 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;
}