题目描述
给定
y
[
]
,
S
y[],S
y[],S,使得
{
∑
x
i
=
S
∀
x
i
,
x
i
⊕
k
=
y
i
\begin{cases} \sum{x_i}=S\\ \forall x_i,x_i\oplus k=y_i \end{cases}
{∑xi=S∀xi,xi⊕k=yi
求最小的
k
k
k
题解
可以进行一个简单的变形
x
i
⊕
k
=
y
i
⇒
x
i
=
y
i
⊕
k
x_i\oplus k=y_i\Rightarrow x_i=y_i\oplus k
xi⊕k=yi⇒xi=yi⊕k
这可以给我们带来什么启发呢?
呃,就是我们可以直接由
y
y
y算
x
x
x对和的贡献。
首先我们这样想
令
f
(
k
)
=
∑
y
i
⊕
k
f(k)=\sum y_i\oplus k
f(k)=∑yi⊕k
将问题转化为求有
f
(
k
)
=
S
f(k)=S
f(k)=S的最小的
k
k
k
发现这个函数的运算是异或,可以想到按二进制的每一位算:
统计
y
[
]
y[]
y[]中二进制第
i
i
i位为
0
/
1
0/1
0/1的个数
c
n
t
[
L
O
G
]
[
0
/
1
]
cnt[LOG][0/1]
cnt[LOG][0/1]
则当
k
k
k的第
i
i
i位为
1
1
1,则对
s
s
s的贡献为
(
1
<
<
i
)
∗
c
n
t
[
i
]
[
0
]
(1<<i)*cnt[i][0]
(1<<i)∗cnt[i][0];
则当
k
k
k的第
i
i
i位为
0
0
0,则对
s
s
s的贡献为
(
1
<
<
i
)
∗
c
n
t
[
i
]
[
1
]
(1<<i)*cnt[i][1]
(1<<i)∗cnt[i][1]。
所以有一个我们可以一位一位的算贡献
先想确定
k
k
k的顺序,对于第
i
i
i位的贡献
(
1
<
<
i
)
∗
c
n
t
(1<<i)*cnt
(1<<i)∗cnt,显然是不会再影响到前
i
i
i位的,所以是从低位到高位计算。
然而第
i
i
i位的贡献是可能影响到之后的,我们把这个理解为进位。
所以差不多可以想到这是一个
d
p
dp
dp了。
状态
d
p
[
i
]
[
d
e
l
]
dp[i][del]
dp[i][del]:确定完前
i
i
i位,向后进位为
d
e
l
del
del的最小
k
k
k
这里的del是进位,具体地说就是实际影响是del*(1<<(i+1))
因为第
i
i
i位之后的都是不会再影响到第
i
i
i位的,所以到第
i
i
i位时一定要满足与
S
S
S的第
i
i
i位相等
实现用刷表法
if(((j+cnt[i+1][0])&1)==((S>>(i+1))&1))
f[i+1][(j+cnt[i+1][0])>>1]=min(f[i+1][(j+cnt[i+1][0])>>1],f[i][j]+(1LL<<(i+1)));
if(((j+cnt[i+1][1])&1)==((S>>(i+1))&1))
f[i+1][(j+cnt[i+1][1])>>1]=min(f[i+1][(j+cnt[i+1][1])>>1],f[i][j]);
代码
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN=int(3e5+5);
#define LOG 60
#define Log 20
typedef long long LL;
#define INF (1LL<<60)
int n;
LL y[MAXN],f[LOG+5][(1<<Log)+5],S;
int cnt[LOG+5][2];
int main()
{
scanf("%d%lld",&n,&S);
for(int i=1;i<=n;i++)
scanf("%lld",&y[i]);
for(int i=1;i<=n;i++)
for(int j=0;j<=LOG;j++) {
if(1&(y[i]>>j))
cnt[j][1]++;
else
cnt[j][0]++;
}
for(int i=0;i<=LOG;i++)
for(int j=0;j<=(1<<Log);j++)
f[i][j]=INF;
if((cnt[0][0]&1)==(S&1))
f[0][cnt[0][0]>>1]=1;
if((cnt[0][1]&1)==(S&1))
f[0][cnt[0][1]>>1]=0;
for(int i=0;i<=LOG-1;i++)
for(int j=0;j<=(1<<Log);j++) {
if(f[i][j]==INF)
continue;
if(((j+cnt[i+1][0])&1)==((S>>(i+1))&1))
f[i+1][(j+cnt[i+1][0])>>1]=min(f[i+1][(j+cnt[i+1][0])>>1],f[i][j]+(1LL<<(i+1)));
if(((j+cnt[i+1][1])&1)==((S>>(i+1))&1))
f[i+1][(j+cnt[i+1][1])>>1]=min(f[i+1][(j+cnt[i+1][1])>>1],f[i][j]);
}
if(f[LOG][0]==INF) {
puts("-1");
return 0;
}
printf("%lld",f[LOG][0]);
}
T h a n k s Thanks Thanks F o r For For R e a d i n g ! Reading! Reading!