题目
思路
我们可以得到:
{
x
1
=
y
1
∧
k
x
2
=
y
2
∧
k
.
.
.
x
n
=
y
n
∧
k
∑
i
=
1
n
x
i
=
S
\begin{cases} x_1=y_1\wedge k\\ x_2=y_2\wedge k\\ ...\\ x_n=y_n\wedge k\\ \sum_{i=1}^n x_i=S \end{cases}
⎩⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎧x1=y1∧kx2=y2∧k...xn=yn∧k∑i=1nxi=S
对所有
y
y
y 第
i
i
i 位为
0
0
0 记为
c
n
t
i
0
cnt_{i0}
cnti0
对所有
y
y
y 第
i
i
i 位为
1
1
1 记为
c
n
t
i
1
cnt_{i1}
cnti1
如果我们
k
k
k 第
i
i
i 位为
0
0
0 ,答案就会增加
(
1
<
<
i
)
∗
c
n
t
i
1
(1<<i)*cnt_{i1}
(1<<i)∗cnti1
如果我们
k
k
k 第
i
i
i 位为
1
1
1 ,答案就会增加
(
1
<
<
i
)
∗
c
n
t
i
0
(1<<i)*cnt_{i0}
(1<<i)∗cnti0
即对于
k
k
k 的每一位要考虑填
0
0
0 或者
1
1
1
如果直接枚举会爆掉
但是我们发现所有
c
n
t
∈
[
0
,
n
]
cnt\in [0,n]
cnt∈[0,n]
发现
n
n
n 最大
18
18
18 位左右
如果认为是从低位到高位确定,把答案增加表示成二进制形式 此时
0
∼
i
0\sim i
0∼i 位的答案统一表示成
(
1
<
<
i
)
∗
t
(1<<i)*t
(1<<i)∗t 的形式 (少的部分就是
S
S
S 最低的
i
−
1
i-1
i−1 位),
i
i
i 位对
i
+
1
i+1
i+1位的答案增加最多只会让位数加1,也就
20
20
20 位左右
而如果我们第
i
i
i 位确定后肯定
i
i
i 位和比
i
i
i 位小的位数都和
S
S
S 一样
于是我们可以反过来通过这
20
20
20 位保存
k
k
k 最小的
于是就有
D
p
Dp
Dp 定义:
f
[
i
]
[
j
]
:
前
i
位
,
第
i
位
对
第
i
+
1
位
进
位
为
j
的
最
小
秘
钥
f[i][j]:前i位,第i位对第i+1位进位为j的最小秘钥
f[i][j]:前i位,第i位对第i+1位进位为j的最小秘钥
代码
#include<set>
#include<map>
#include<stack>
#include<cmath>
#include<ctime>
#include<queue>
#include<cstdio>
#include<vector>
#include<climits>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
LL read(){
bool f=0;LL x=0;char c=getchar();
while(c<'0'||'9'<c){if(c=='-')f=1;c=getchar();}
while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return !f?x:-x;
}
#define MAXN 300000
#define INF 4600000000000000000ll
LL num[MAXN+5],cnt[100][2],f[65][(1<<20)+5];
int main(){//4611686018427387903
int n=read();LL S=read();
for(int i=1;i<=n;i++)//f[i][j]:前i位,第i位对第i+1位进位为j的最小秘钥
num[i]=read();
for(int j=0;j<=60;j++)
for(int i=1;i<=n;i++)
cnt[j][(num[i]>>j)&1]++;
for(int i=0;i<=60;i++)
for(int j=0;j<=(1<<20);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<60;i++){
LL t=(1ll<<(i+1));
for(int j=0;j<=(1<<20);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]+t);
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[60][0]==INF) puts("-1");
else printf("%lld\n",f[60][0]);
return 0;
}
思考
考试时想到了 c n t cnt cnt 很小,但是思路很接近,往状压方面想了,但就是没写出来,反映出对状压不熟悉。