[bzoj2734][HNOI2012]集合选数——矩阵+状压DP 大佬们的博客 Some Links

版权声明:欢迎大家转载,转载请标明出处。 https://blog.csdn.net/ylsoi/article/details/82049630

题目大意:

给定一个集合,里面为1-n的数,现在要求从中选择一些数,选了x就不可以选2x和3x,求方案数。

思路:

考虑根据题目的限制关系构造一个矩阵。

[ x , 2 x , 4 x , 8 x 3 x , 6 x , 12 x 9 x , 18 x , 36 x ]

每一行里面的关系是 × 2 ,每一列里面的关系是 × 3 ,不难发现这个矩阵里面相邻的数我们都不可以选择,所以可以用状压DP来解决。
如果我们把x设为1,那么肯定还有很多数没有包括在这个里面,其实没有包括在这个里面的数和这个矩阵没有任何约数的关系,它们互相不可到达,同时每一个数也只会在一个矩阵里面。所以我们可以对于没有在这里面的数再构造若干个矩阵,直至所有的数都计算完为止。
最后的答案为若干个矩阵的答案之积。

#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
typedef long long ll;

using namespace std;

void File(){
    freopen("bzoj2734.in","r",stdin);
    freopen("bzoj2734.out","w",stdout);
}

void out(int x,int len){
    int qu[70]={0},q=0;
    while(len--)qu[++q]=x%2,x/=2;
    DREP(i,q,1)printf("%d ",qu[i]);
    putchar('\n');
}

const int maxn=1e5+10;
const int maxb=20;
const int maxw=(1<<18)+10;
const ll mod=1e9+1;
int n,h,len[maxb],a[maxb][maxb];
int all[maxb],st[maxw],tot;
ll dp[maxb][maxw],ans=1;
bool usd[maxn];

void add(ll &_,ll __){_=_+__;if(_>mod)_-=mod;}

ll solve(int x){
    h=1; a[1][1]=x;
    while(a[h][1]*3<=n)a[h+1][1]=a[h][1]*3,++h;
    REP(i,1,h){
        len[i]=1;
        while(a[i][len[i]]*2<=n)a[i][len[i]+1]=a[i][len[i]]*2,++len[i];
        all[i]=(1<<len[i])-1;
    }
    REP(i,1,h)REP(j,1,len[i])usd[a[i][j]]=1;
    dp[0][0]=1;
    REP(i,1,h){
        REP(k,1,tot){
            if(st[k]>all[i])break;
            dp[i][st[k]]=0;
            REP(j,1,tot){
                if(st[j]>all[i-1])break;
                if(!(st[j]&st[k]))
                    add(dp[i][st[k]],dp[i-1][st[j]]);
            }
        }
    }
    ll ret=0;
    REP(i,1,tot){
        if(st[i]>all[h])break;
        add(ret,dp[h][st[i]]);
    }
    return ret;
}

int main(){
    File();
    scanf("%d",&n);
    REP(S,0,(1<<17)-1){
        bool flag=true;
        REP(i,2,17)if(((1<<(i-1))&S) && ((1<<(i-2))&S))
            flag=false;
        if(flag)st[++tot]=S;
    }
    REP(i,1,n)if(!usd[i]){
        usd[i]=1;
        ans=ans*solve(i)%mod;
    }
    printf("%lld\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/ylsoi/article/details/82049630