P2396 已预约 了哦v额是 吗啊他好是 VII

题目大意

给定数组 \(a\) 和 \(b\),\(|a|=n,|b|=m\),有一个 \(k\),起初 \(k=0\),每次可以选一个没有被选过的 \(i\le n\),使 \(k\larr k+a_i\),任意时刻 \(\forall j,k\neq b_j\),求方案数。

\(n\le 24,m\le 2\)。

题目思路

0x01 我会全排列!

枚举全排列即可。

时间复杂度:\(O(n!n)\)。

预计得分:滚木。

0x02 我会DP!

我们发现,对于一个 \(a_i\),它前面用的数的顺序有滚木的作用,我们只需看到最后的和,而且 \(n\) 也好小,考虑状压。

\(dp_{mask}\) 代表用的数字状态为 \(mask\) 时的方案数。

转移方程为:

\[dp_S=\begin{cases} 0 & sum_S=b_j\\ \sum_{T\sube S} dp_T & \text{otherwise} \end{cases} \]

时间复杂度:\(O(2^nn)\)

预计得分:50pts。

0x03 我会lowbit!

众所周知,lowbit 可以优化状压 DP 的常数。

我们在枚举每一位的时候,很多时候会枚举到有滚木作用的位上,我们使用 \(lowbit\),就可以卡掉大约一半的常数。

比如:\(sum_S=sum_{S\ xor\ lowbit(S)}+a_{log_2lowbit(x)}\)

代码:

dp[0]=1;
for(int mask=1;mask<(1<<n);mask++){
    dis[mask]=dis[mask^lowbit(mask)]+dis[lowbit(mask)];
    if(check(dis[mask])) continue;
	for(int i=mask;i;i-=lowbit(i)){//枚举所有为 1 的位
        int k=lowbit(i);
        (dp[mask]+=dp[mask^k])%=mod;
    }
}

时间复杂度:\(O(2^nn)\) 就像树状数组暴打线段树一样,常数极小。

预计得分:100pts。

posted @ 2026-10-28 17:31  CAN_NOT_AC  阅读(22)  评论(1)    收藏  举报