C. Array Elimination(逻辑运算&)
题意:
给出一组数,对于一个数k,每次可以选择k个位置进行若干次操作:k个位置上的数减去这k个数的与值。
求出所有能够使得这组数都变为 0 的数 k。
思路:
要把所有数都变为0,也就是将所有数的数位变为0。
每次操作,减去的是k个数的与值。所以,为了将一个数位由1变为0,减去的这个与值的该数位要为1。
为了让与值的该数位为1,那么挑选的k个位置上的数该数位都为1。
因为可以操作若干次,所以对于一列数位的 x 个1来说,可以选择 x 的因子 y,能够将y个数的该数位都化为0,然后进行若干次,就可以将 x 个数的该数位都变为 0。
综合考虑所有数位,那么满足条件的 k 就为30个数位中,当前位为 1 的数的个数的公约数。
一个数的某数位为1,减去一个该数位同样为1的数,这个数位就变成0了。
求一组数的公约数 = 求其gcd的所有因子。
对于这样位运算的题,不要看整个数,要看每个数位之间的操作和变化。
Code:
const int N = 200010, mod = 1e9+7;
int T, n, m, a[N], ans[N];
int f[35];
void pd(int x)
{
for(int i=0;i<=30;i++)
{
if(x&(1<<i)) f[i]++;
}
}
void prim(int x)
{
int cnt=0;
for(int i=1;i<=x/i;i++)
{
if(x%i==0){
ans[++cnt]=i;
if(x/i!=i) ans[++cnt]=x/i;
}
}
sort(ans+1,ans+cnt+1);
for(int i=1;i<=cnt;i++) cout<<ans[i]<<" ";
cout<<endl;
}
int main(){
Ios;
cin>>T;
while(T--)
{
cin>>n;
mem(f,0);
for(int i=1;i<=n;i++){
int x;cin>>x;
pd(x);
}
int g;
for(int i=0;i<=30;i++){
if(i==0) g=f[i];
else g=__gcd(g,f[i]);
}
if(!g) for(int i=1;i<=n;i++) cout<<i<<" ";
prim(g);
}
return 0;
}