把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

CF1689E ANDfinity

题面传送门

首先发现如果\(A_i=0\),则一定要花费一步将\(A_i\)加一。

给出结论:之后只会操作两次。

我们记最低位最高的数的集合为\(S\),则若\(|S|=1\),直接将\(S\)减一,则所有数都被联通了。

\(|S|>1\),则所有没被减的数不与减了的数联通,因为这些本来是联通的,这样的话只需要将其中一个没有被减了的数加一即可。

对于答案为\(1\)的直接爆枚那个被加了/减了然后check即可,时间复杂度\(O(n^2\log A)\)

code:

// LUOGU_RID: 94306371
#include<bits/stdc++.h>
#define Gc() getchar() 
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define R(n) (rnd()%(n))
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using ll=long long;using db=double;using lb=long db;using ui=unsigned;using ull=unsigned ll;using u128=__int128;
using namespace std;const int N=2e3+5,M=4e3+5,K=2e6+5,mod=1e9+7,Mod=mod-1;ll INF=1e18+7;const db eps=1e-5;mt19937 rnd(time(0));
int Mx,n,A[N],Ct,fa[30],Fl[30];
int GF(int x){return fa[x]^x?fa[x]=GF(fa[x]):x;}
int CK(){
	int i,j;Me(Fl,0);for(i=0;i<30;i++) fa[i]=i;for(i=1;i<=n;i++){if(!A[i]) return 0;
		int Fr=-1;for(j=0;j<30;j++) if(A[i]>>j&1) {Fl[j]=1;if(Fr==-1) Fr=j;else fa[GF(j)]=GF(Fr);}
	}int Ct=0;for(i=0;i<30;i++) if(GF(i)==i&&Fl[i]) Ct++;return Ct==1; 
}
void GA(int Ans){printf("%d\n",Ans);for(int i=1;i<=n;i++) printf("%d ",A[i]);Pc('\n');}
void Solve(){
	int i,j;Ct=0;scanf("%d",&n);for(i=1;i<=n;i++) scanf("%d",&A[i]),!A[i]&&(Ct++,A[i]=1);if(CK())return GA(Ct);
	for(i=1;i<=n;i++) {A[i]++;if(CK()) return GA(Ct+1);A[i]-=2;if(A[i]>=0&&CK()) return GA(Ct+1);A[i]++;}
	Mx=1;for(i=2;i<=n;i++) if((A[i]&-A[i])>(A[Mx]&-A[Mx])) Mx=i;A[Mx]--;
	for(i=Mx+1;i<=n;i++) if((A[i]&-A[i])==((A[Mx]+1)&-(A[Mx]+1))) {A[i]++;break;}
	if(CK()) return GA(Ct+2);assert(0);
}
int main(){
//	freopen("1.in","r",stdin); 
	int T;scanf("%d",&T);while(T--) Solve();
}
posted @ 2022-11-17 09:17  275307894a  阅读(18)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end