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。

浙公网安备 33010602011771号