2734: [HNOI2012]集合选数

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;
}

猜你喜欢

转载自www.cnblogs.com/mjtcn/p/10467205.html