CF1689E ANDfinity 题解
CF1689E ANDfinity 题解
对于图的连通性,可以用并查集维护(合并成功 次,或者一个集合大小为 ,则整张图已经连通)。
首先有很显然:所有的 都要变成 ,因为 和任何数按位与都是 。
此时如果整张图已经连通,那么直接结束。
不连通的话呢?有个性质:只通过对偶数的最多两次操作,便一定达成整张图连通。
那么为了答案最优,对于一次操作,暴力扫就可以。两次操作的构造方式如下。
方法与证明:
首先所有奇数肯定在同一个连通块里,因为它们二进制形式的末尾是 。这个连通块内也可能有偶数。
所以除开包含奇数的连通块,其它零散的连通块肯定全是偶数。
考虑加一和减一操作对一个偶数二进制形式的影响:
- 加一则把偶数的末尾的 变成 ;
- 减一会把它的
lowbit
变成 ,后面的都变成 。
可以看到,加一只会加边不会删边,而减一加的边可能更多但是可能删边。
注意第二条,贪心的想,可以把最大的 lowbit
对应的数减一,这样 lowbit
小于它的其它数都会与它连通,并且它们都和奇数连通块连通了。此时场上最多两个连通块,并且一个是包含奇数的连通块,另一个是 lowbit
全都是最大值的连通块,操作的数和此块可能断绝了联系。
同时可以发现,如果最大的 lowbit
只有一个,那么已经连通;如果不含奇数的连通块只有一个,通过加一操作就可以达成连通。这些在之前暴力枚举中已经解决。
如果此时还未整个连通,剩下一个无奇数连通块只要加一,根据前面的讨论,这样只会加边,而且它会带着这个连通块和那个奇数连通块连通,那么此时就达成了目标。
一次操作的已经暴力实现,两次操作的这样一定可以构造出来。
于是就证明完了。
丑陋的代码 qwq:
#include<bits/stdc++.h>
#define EL puts("Elaina")
#define reg register int
typedef long long ll;
using namespace std;
inline char gc(){
static char buf[1<<20],*p1,*p2;
if(p1==p2){p1=buf,p2=buf+fread(buf,1,1<<20,stdin);if(p1==p2)return EOF;}
return *p1++;
}
inline int read(){
int x=0,f=1;char ch=gc();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}
while(isdigit(ch))x=(x<<1)+(x<<3)+(ch^48),ch=gc();
return x;
}
const int maxn=2e3+3;
inline int lowbit(int x){return x&(-x);}
int n,a[maxn],ans,cnt;
struct dsu{
int fa[maxn],rk[maxn],val[maxn];
inline void init(int n){
for(reg i=1;i<=n;++i)fa[i]=i,rk[i]=0,val[i]=a[i];
}
inline int find(int x){
return fa[x]^x?fa[x]=find(fa[x]):x;
}
inline bool merge(int x,int y){
x=find(x),y=find(y);
if(x==y)return 0;
if(rk[x]<rk[y])fa[x]=y,rk[y]=max(rk[y],rk[x]+1),val[y]|=val[x];
else fa[y]=x,rk[x]=max(rk[x],rk[y]+1),val[x]|=val[y];
return 1;
}
inline int value(int x){
return val[find(x)];
}
}S;
inline bool link(){
cnt=0,S.init(n);
for(reg i=1;i<=n;++i)
for(reg j=1;j<i;++j)
if(a[i]&a[j]){
cnt+=S.merge(i,j);
if(cnt==n-1)return 1;
}
return 0;
}
inline void overdesu(){
printf("%d\n",ans);
for(reg i=1;i<=n;++i)
printf("%d ",a[i]);
printf("\n");
}
inline void MyDearMomonts(){
n=read(),ans=0,cnt=0;
for(reg i=1;i<=n;++i){
a[i]=read();
if(!a[i])ans+=++a[i];
}
S.init(n);
if(link()){overdesu();return;}
for(reg i=1;i<=n;++i){
a[i]++,ans++;
if(link()){overdesu();return;}
a[i]-=2;
if(link()){overdesu();return;}
a[i]++,ans--;
}
int hib=0;
for(reg i=1;i<=n;++i)
hib=max(hib,lowbit(a[i]));
for(reg i=1;i<=n;++i)
if(lowbit(a[i])==hib){
a[i]--,ans++;
if(link()){overdesu();return;}
break;
}
for(reg i=1;i<=n;++i)
if(!(S.value(i)&1)){
a[i]++,ans++,overdesu();
return;
}
}
int main(){
int T=read();
while(T--)MyDearMomonts();
return (0^0);
}