题意:
现在有n个数,让你求一个数,让这些数异或上这个数,得到的新数组,每个数二进制下1的个数相同。
题解:
和我们这次新生校赛上的一道题目很像,解法都是相同的。
首先要想到将每一位分解出来,也就是第2的i次方这位取的话,对这些数的影响是什么,如果这一位上有的话,那么就是-1,否则+1.然后怎么将每个数都变成1个数相同的呢,我们只需要枚举0-30这些情况,然后检查我们异或之后是不是这个情况,很明显不能将每个数组存下来,需要用哈希存到map中。然后看每一位取不取的话,我们不能直接做,一因为这样是
种情况,一般出现这样子的情况的话,就是使用对半的方法。我们处理出前15位的情况,存到map中,然后做后15位的时候,对于每一种情况我都检查一遍0-30这些是否可能。
#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long
#define pa pair<ull,ull>
int a[105],b[35][105],c[105];
map<pa,int>mp;
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),c[i]=a[i];
for(int i=0;i<30;i++){
for(int j=1;j<=n;j++){
if(a[j]&(1<<i))
b[i][j]=-1;
else
b[i][j]=1;
}
}
for(int i=1;i<=n;i++)a[i]=__builtin_popcount(a[i]);
for(int i=1;i<(1<<15);i++){
pa h={0,0};
int t[105]={0};
for(int j=0;j<=14;j++){
if(i&(1<<j))
for(int k=1;k<=n;k++)
t[k]+=b[j][k];
}
for(int j=1;j<=n;j++)
h={h.first*31ll+(ull)(28+t[j]),h.second*23ll+(ull)(28+t[j])};
mp[h]=i;
}
mp[{0,0}]=0;
int t[105];
for(int i=0;i<(1<<15);i++){
pa h={0,0};
for(int j=1;j<=n;j++)t[j]=a[j];
for(int j=0;j<=14;j++){
if(i&(1<<j))
for(int k=1;k<=n;k++)
t[k]+=b[j+15][k];
}
for(int bit=0;bit<=30;bit++){
h={0,0};
for(int j=1;j<=n;j++)
h={h.first*31ll+(ull)(28ll+bit-t[j]),h.second*23ll+(ull)(28ll+bit-t[j])};
if(mp.count(h)){
int pre=mp[h],aft=i;
int ans=pre+(aft<<15);
printf("%d\n",ans);
//for(int i=1;i<=n;i++)
//printf("%d%c",__builtin_popcount(ans^c[i])," \n"[i==n]);
return 0;
}
}
}
printf("-1\n");
return 0;
}
/*
3
324994 321920 371974
*/