Description
小Q发明了一个新的加密算法,对于一个长度为
n
n
n的非负整数序列
a
1
,
a
2
,
.
.
.
,
a
n
a_1,a_2,...,a_n
a1,a2,...,an,他会随机选择一个非负整数k,
将每个数都异或上
k
k
k得到
b
1
,
b
2
,
.
.
.
,
b
n
b_1,b_2,...,b_n
b1,b2,...,bn,即
b
i
=
a
i
x
o
r
k
b_i=a_i\ xor\ k
bi=ai xor k。不幸的是,健忘的小Q睡了一觉之后就把密钥
k
k
k忘得
一干二净了,不过他隐约记得
a
1
+
a
2
+
.
.
.
+
a
n
a_1+a_2+...+a_n
a1+a2+...+an的值为
m
m
m,你能帮他找到一个可行的密钥吗
Input
第一行包含两个整数
n
,
m
(
1
<
=
n
<
=
100000
,
0
<
=
m
<
2
60
)
n,m(1<=n<=100000,0<=m<2^{60})
n,m(1<=n<=100000,0<=m<260),分别表示序列的长度以及加密前所有数的和。
第二行包含
n
n
n个整数
b
1
,
b
2
,
.
.
.
,
b
n
(
0
<
=
b
i
<
2
60
)
b_1,b_2,...,b_n(0<=b_i<2^{60})
b1,b2,...,bn(0<=bi<260),表示加密后的序列。
Output
输出一个非负整数k,若无解输出-1,若有多组解,输出最小的 k k k。
Sample Input
3 5
1 2 3
Sample Output
1
分析:
题目大意就是找一个数
k
k
k,使得给定数列都异或
k
k
k后和为
m
m
m。
对于每一位,我们考虑这一为是0还是1的贡献,0为
c
n
t
[
i
]
∗
2
i
cnt[i]*2^i
cnt[i]∗2i,1为
(
n
−
c
n
t
[
i
]
)
∗
2
i
(n-cnt[i])*2^i
(n−cnt[i])∗2i,其中
c
n
t
[
i
]
cnt[i]
cnt[i]为从低位起所有数第
i
i
i位1的个数和。
考虑第
i
i
i位答案选0或选1,设
f
[
i
]
[
j
]
f[i][j]
f[i][j]为选到第
i
i
i位,比
m
m
m小
j
∗
2
i
j*2^i
j∗2i的最小答案。
考虑从高位进行dp,设
m
m
m第
i
i
i位为
a
[
i
]
a[i]
a[i]。
第
i
i
i位选0,
f
[
i
]
[
j
∗
2
+
a
[
i
]
−
c
n
t
[
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],第
i
i
i位选1,
f
[
i
]
[
j
∗
2
+
a
[
i
]
−
n
+
c
n
t
[
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]。
然后可以滚掉
i
i
i。
至于第二维,当
j
>
2
n
j>2n
j>2n时,后面无论怎样都不可能使得它变为0,所以只要保留
2
n
2n
2n即可。
代码:
/**************************************************************
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]);
}