[HNOI2012]集合选数(状压DP+构造)

题目要求若出现x,则不能出现2x,3x

所以我们考虑构造一个矩阵

\(1\ 2\ 4 \ 8……\)

\(3\ 6\ 12\ 24……\)

\(9\ 18\ 36……\)

\(……\)

不难发现,对于一个矩阵,若我选择了一个数x,则在矩阵内该数的相邻格子都不能选,题目就被转化成了玉米田了,可以用状压DP解决

但是直接做是不对的,比如5就没有出现在这个序列中

所以我们可以构造多个矩阵,用乘法原理统计答案即可

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define re register
#define debug printf("Now is Line : %d\n",__LINE__)
#define file(a) freopen(#a".in","r",stdin);//freopen(#a".out","w",stdout)
#define int long long
#define inf 123456789
#define mod 1000000001
il int read() {
    re int x = 0, f = 1; re char c = getchar();
    while(c < '0' || c > '9') { if(c == '-') f = -1; c = getchar();}
    while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
    return x * f;
}
#define rep(i, s, t) for(re int i = s; i <= t; ++ i)
#define drep(i, s, t) for(re int i = t; i >= s; -- i)
#define mem(k, p) memset(k, p, sizeof(k))
#define maxn 100005
int n, m, a[20][20], g[1 << 15], vis[maxn], H, L[20], dp[20][1 << 15], ans = 1;
il void martix(int x) {
    H = 0;
    rep(i, 1, 18) {
        a[i][1] = (i == 1) ? x : a[i - 1][1] * 2;
        if(a[i][1] > n) break;
        ++ H, L[i] = vis[a[i][1]] = 1;
        rep(j, 2, 11) {
            a[i][j] = a[i][j - 1] * 3;
            if(a[i][j] > n) break;
            L[i] = j, vis[a[i][j]] = 1;
        }
    }
}
il int solve() {
    rep(i, 0, (1 << L[1]) - 1) dp[1][i] = g[i];
    rep(i, 2, H) {
        rep(j, 0, (1 << L[i]) - 1) {
            if(!g[j]) continue;
            dp[i][j] = 0;
            rep(k, 0, (1 << L[i - 1]) - 1) {
                if(g[k] && (k & j) == 0) dp[i][j] += dp[i - 1][k];
            }
        }
    }
    int t = 0;
    rep(i, 0, (1 << L[H]) - 1) t = (t + dp[H][i]) % mod;
    return t;
}
signed main() {
    n = read();
    rep(i, 0, (1 << 11) - 1) g[i] = !(i & (i << 1));
    rep(i, 1, n) if(!vis[i]) martix(i), ans = ans * solve() % mod;
    printf("%lld", ans);
    return 0;
}
posted @ 2019-04-29 10:16  呢没理他  阅读(240)  评论(0编辑  收藏  举报