加密[CSP2019模拟 Day 4](状压dp)

mac2026-05-07  7

文章目录

题目思路代码思考

题目

思路

我们可以得到: { 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=y1kx2=y2k...xn=ynki=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 0i 位的答案统一表示成 ( 1 < < i ) ∗ t (1<<i)*t (1<<i)t 的形式 (少的部分就是 S S S 最低的 i − 1 i-1 i1 位), 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,ii+1j

代码

#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 很小,但是思路很接近,往状压方面想了,但就是没写出来,反映出对状压不熟悉。

最新回复(0)