luogu-P3226题解
简要题意
对于任意一个正整数 \(n \le 10^5\),如何求出 \(\{1,2,\ldots ,n\}\) 的满足约束条件的子集的个数。约束条件:若 \(x\) 在该子集中,则 \(2x\) 和 \(3x\) 不能在该子集中。
数据范围:\(1 \le n \le 10^5\)。
题解
若我们将约束条件将互相有影响的数建成图,我们会惊喜地发现 \(n\) 个数会被划分到若干集合中,并且集合间独立。所以问题简化成只考虑一个集合的合法方案数。下面我们以一所在集合为例进行分析。
我们先列出集合 \(\{1,2,3,4,6,8,9,12,16,18\ldots \}\),并且把有直接影响的数连边,我们尝试在方格图上求解问题。
\[\begin{aligned}
&1 & 2 && 4 && 8 && 16\\
&3 & 6 && 12\\
&9 & 18
\end{aligned}
\]
此时约束条件等价于选方格,要求选中的方格不相邻的方案数。可以考虑枚举一维,状压一维,且方格图不大,所以可通过此题。
代码
bool chk(int x, int y){return ! (x & y);}
void solve(int st){
res = 0;
for(int i = 1, j; ; ++i){
a[i][1] = i ^ 1 ? a[i - 1][1] * 3 : st;
if(a[i][1] > n)break; m = i;
vs.set(a[i][1]);
for(j = 2; ; ++j){
a[i][j] = a[i][j - 1] * 2;
if(a[i][j] > n)break;
vs.set(a[i][j]);
}
lim[i] = j - 1;
}
for(int i = 0; i < (1 << lim[1]); ++i)f[1][i] = g[i];
for(int i = 2; i <= m; ++i)for(int j = 0; j < (1 << lim[i - 1]); ++j)if(g[j])
for(int k = 0; k < (1 << lim[i]); ++k)if(g[k] and chk(j, k))(f[i][k] += f[i - 1][j]) %= p;
for(int i = 0; i < (1 << lim[m]); ++i)(res += f[m][i]) %= p;
for(int i = 2; i <= m; ++i)for(int j = 0; j < (1 << lim[i]); ++j)f[i][j] = 0;
}

浙公网安备 33010602011771号