bzoj 2734 [HNOI2012]集合选数 构造+状压dp
题面
解法
本题解法十分精妙
构造矩阵,保证每一行是一个公比为3的数列,每一列是一个公比为2的数列
发现这个矩阵最多不超过17行11列
那么我们可以枚举不是2和3公倍数的数放在\((1,1)\)的位置,然后构造出该矩阵
设\(f_{i,j}\)表示第\(i\)行选取方案为\(j\)的方案数
转移一下即可
代码
#include <bits/stdc++.h>
#define Mod 1000000001
using namespace std;
template <typename node> void read(node &x) {
x = 0; int f = 1; char c = getchar();
while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
int tx, lim[20], vis[100010], num[20][20], f[20][1 << 12];
int solve(int x) {
int n = 18, m = 11;
num[1][1] = x, lim[1] = 0;
for (int i = 2; i <= n; i++)
num[i][1] = min(tx + 1, num[i - 1][1] * 2), lim[i] = 0;
for (int i = 1; i <= n; i++)
for (int j = 2; j <= m; j++)
num[i][j] = min(tx + 1, num[i][j - 1] * 3);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
if (num[i][j] <= tx) lim[i] |= 1 << j - 1, vis[num[i][j]] = 1;
for (int i = 0; i <= n; i++)
for (int j = 0; j <= lim[i]; j++)
f[i][j] = 0;
f[0][0] = 1;
for (int i = 0; i <= n; i++)
for (int j = 0; j <= lim[i]; j++) {
if (!f[i][j]) continue;
for (int k = 0; k <= lim[i + 1]; k++) {
if ((j & k) || (k & (k << 1))) continue;
f[i + 1][k] = (f[i + 1][k] + f[i][j]) % Mod;
}
}
return f[n][0];
}
int main() {
int ans = 1; read(tx);
for (int i = 1; i <= tx; i++)
if (!vis[i]) ans = (1ll * ans * solve(i)) % Mod;
cout << ans << "\n";
return 0;
}