Description
小Q发明了一个新的加密算法,对于一个长度为n的非负整数序列a_1,a_2,...,a_n,他会随机选择一个非负整数k,
将每个数都异或上k得到b_1,b_2,...,b_n,即b_i=a_i xor k。不幸的是,健忘的小Q睡了一觉之后就把密钥k忘得
一干二净了,不过他隐约记得a_1+a_2+...+a_n的值为m,你能帮他找到一个可行的密钥吗
Input
第一行包含两个整数n,m(1<=n<=100000,0<=m<2^{60}),分别表示序列的长度以及加密前所有数的和。
第二行包含n个整数b_1,b_2,...,b_n(0<=b_i<2^{60}),表示加密后的序列。
Output
输出一个非负整数k,若无解输出-1,若有多组解,输出最小的k。
从高位到低位dp确定k的值,状态表示为当前考虑完第i位,m已被凑出了j,此时k的最小值。观察到$j$是$2^i$的倍数,且当$\frac{j}{2^i}$在O(n)范围时才对最终答案有影响,因此总状态数可以接受,在O(nlogm)级别。
#include<bits/stdc++.h> typedef long long i64; const int N=2e5+7; const i64 inf=1ll<<60; char ib[N*30],*ip=ib; i64 _(){ i64 x=0; while(*ip<48)++ip; while(*ip>47)x=x*10+*ip++-48; return x; } int n,t[65][2]; i64 m,f[N],g[N]; void mins(i64&a,i64 b){if(a>b)a=b;} int main(){ fread(ib,1,sizeof(ib),stdin); n=_(),m=_(); for(int i=1;i<=n;++i){ i64 x=_(); for(int j=0;j<60;++j)++t[j][x>>j&1]; } n*=2; for(int i=1;i<=n;++i)f[i]=inf; for(int i=59;i>=0;--i){ for(int j=m>>i&1;j<=n;j+=2)g[j]=f[j>>1]; for(int j=0;j<=n;++j)f[j]=inf; int c1=t[i][0],c0=t[i][1]; for(int j=m>>i&1;j<=n;j+=2)if(g[j]<inf){ if(j>=c0)mins(f[j-c0],g[j]); if(j>=c1)mins(f[j-c1],g[j]|1ll<<i); } } printf("%lld\n",f[0]==inf?-1ll:f[0]); return 0; }