2734: [HNOI2012]集合选数
分析:
转化一下题意。
1 3 9 27...
2 6 18 54...
4 12 36 108...
8 24 72 216...
...
写成这样的矩阵阵后,那么题意就是不能选相邻的点,求方案数。可以知道行不超过18,列不超过11,然后状压dp即可。
发现5在这个矩阵中没有出现,于是可以在构造a[1][1]=5的矩阵,利用乘法原理求出相乘。同样地,构成a[1][1]为没有出现的数的矩阵,相乘。
代码:
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<cmath> #include<cctype> #include<set> #include<queue> #include<vector> #include<map> using namespace std; typedef long long LL; inline int read() { int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1; for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f; } const int mod = 1000000001; int a[20][20], b[20], f[20][2100], n; bool vis[100005]; inline void add(int &x,int y) { x += y; if (x >= mod) x -= mod; } int Calc(int x) { memset(b, 0, sizeof(b)); a[1][1] = x; for (int i = 2; i <= 18; ++i) if ((a[i - 1][1] << 1) <= n) a[i][1] = a[i - 1][1] << 1; else a[i][1] = n + 1; for (int i = 1; i <= 18; ++i) for (int j = 2; j <= 11; ++j) if (a[i][j - 1] * 3 <= n) a[i][j] = a[i][j - 1] * 3; else a[i][j] = n + 1; for (int i = 1; i <= 18; ++i) for (int j = 1; j <= 11; ++j) if (a[i][j] <= n) b[i] += (1 << (j - 1)), vis[a[i][j]] = 1; for (int i = 1; i <= 18; ++i) for (int j = 0; j <= b[i]; ++j) f[i][j] = 0; f[0][0] = 1; for (int i = 0; i < 18; ++i) for (int s = 0; s <= b[i]; ++s) if (f[i][s]) for (int t = 0; t <= b[i + 1]; ++t) if ((s & t) == 0 && (t & (t >> 1)) == 0) add(f[i + 1][t], f[i][s]); return f[18][0]; } int main() { n = read(); LL ans = 1; for (int i = 1; i <= n; ++i) if (!vis[i]) ans = 1ll * ans * Calc(i) % mod; cout << ans; return 0; }