Description
小Q发明了一个新的加密算法,对于一个长度为nnn的非负整数序列a1,a2,...,ana_1,a_2,...,a_na1,a2,...,an,他会随机选择一个非负整数k,
将每个数都异或上kkk得到b1,b2,...,bnb_1,b_2,...,b_nb1,b2,...,bn,即bi=ai xor kb_i=a_i\ xor\ kbi=ai xor k。不幸的是,健忘的小Q睡了一觉之后就把密钥kkk忘得
一干二净了,不过他隐约记得a1+a2+...+ana_1+a_2+...+a_na1+a2+...+an的值为mmm,你能帮他找到一个可行的密钥吗
Input
第一行包含两个整数n,m(1<=n<=100000,0<=m<260)n,m(1<=n<=100000,0<=m<2^{60})n,m(1<=n<=100000,0<=m<260),分别表示序列的长度以及加密前所有数的和。
第二行包含nnn个整数b1,b2,...,bn(0<=bi<260)b_1,b_2,...,b_n(0<=b_i<2^{60})b1,b2,...,bn(0<=bi<260),表示加密后的序列。
Output
输出一个非负整数k,若无解输出-1,若有多组解,输出最小的kkk。
Sample Input
3 5
1 2 3
Sample Output
1
分析:
题目大意就是找一个数kkk,使得给定数列都异或kkk后和为mmm。
对于每一位,我们考虑这一为是0还是1的贡献,0为cnt[i]∗2icnt[i]*2^icnt[i]∗2i,1为(n−cnt[i])∗2i(n-cnt[i])*2^i(n−cnt[i])∗2i,其中cnt[i]cnt[i]cnt[i]为从低位起所有数第iii位1的个数和。
考虑第iii位答案选0或选1,设f[i][j]f[i][j]f[i][j]为选到第iii位,比mmm小j∗2ij*2^ij∗2i的最小答案。
考虑从高位进行dp,设mmm第iii位为a[i]a[i]a[i]。
第iii位选0,f[i][j∗2+a[i]−cnt[i]]=f[i+1][j]f[i][j*2+a[i]-cnt[i]]=f[i+1][j]f[i][j∗2+a[i]−cnt[i]]=f[i+1][j],第iii位选1,f[i][j∗2+a[i]−n+cnt[i]]=f[i+1][j]f[i][j*2+a[i]-n+cnt[i]]=f[i+1][j]f[i][j∗2+a[i]−n+cnt[i]]=f[i+1][j]。
然后可以滚掉iii。
至于第二维,当j>2nj>2nj>2n时,后面无论怎样都不可能使得它变为0,所以只要保留2n2n2n即可。
代码:
/**************************************************************
Problem: 5043
User: ypxrain
Language: C++
Result: Accepted
Time:792 ms
Memory:5196 kb
****************************************************************/
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#define LL long long
const int maxn=1e5+7;
const LL inf=2e18;
using namespace std;
int n;
LL m;
LL a[maxn],f[2][2*maxn],M[62],sum[62];
int main()
{
scanf("%d%lld",&n,&m);
for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
for (int j=0;j<60;j++,m>>=1) M[j]=m&1;
for (int i=1;i<=n;i++)
{
for (int j=0;j<60;j++,a[i]>>=1) sum[j]+=a[i]&1;
}
for (int i=0;i<=2*n;i++) f[0][i]=inf;
f[0][0]=0;
int x=0,y=1;
LL d=(LL)1<<59;
for (int j=59;j>=0;j--,x^=1,y^=1,d>>=1)
{
for (int i=0;i<=2*n;i++) f[y][i]=inf;
for (int i=0;i<=2*n;i++)
{
if (f[x][i]>=inf) continue;
int k=i*2+M[j]-sum[j];
if ((k>=0) && (k<=2*n)) f[y][k]=min(f[y][k],f[x][i]);
k=i*2+M[j]-n+sum[j];
if ((k>=0) && (k<=2*n)) f[y][k]=min(f[y][k],f[x][i]+d);
}
}
if (f[x][0]>=inf) printf("-1");
else printf("%lld",f[x][0]);
}