[BZOJ2734] [HNOI2012]集合选数

[BZOJ2734] [HNOI2012]集合选数

蒻得不行的我觉得这是一道比较难的题,以至于我卡了很久

可以看出,所有会互相直接造成影响的数之间构成一张\(DAG\),边就是\(i->i*2,i->i*3\)

取出每一个连通块之后,连了边的点不能同时选,就是一个独立集个数的问题

\(DAG\)还可以求独立集?

我们其实可以惊人得发现,这张\(DAG\)过于整齐,每个点都是至多两条出边,就是一个网格图,转化成一张网格图上相邻的点不能取的问题

这个,状压矩阵即可\(dp[i][S]\)


const int N=1e5+10,P=1e9+1;
 
 
 
int n;
ll dp[20][1<<11];
int A[20];
 
ll Solve(int i){ 
    int t=0;
    A[0]=0;
    dp[0][0]=1;
    for(;i<=n;i*=2) {//网格图的列数
        t++;
        A[t]=0;
        int c=0;
        for(int j=i;j<=n;j*=3) A[t]|=1<<(c++); //取出网格图这一行的大小
        rep(j,0,A[t]) dp[t][j]=0;
        rep(S1,0,A[t-1]) {
            int fl=1;
            rep(j,0,c+1) if((S1&(1<<j)) && (S1&(1<<(j+1)))) fl=0;
            if(!fl) continue;
            int R=A[t]^(S1&A[t]);
            for(reg int S2=R;;S2=(S2-1)&R) {
                int fl=1;
                rep(j,0,c-1) if((S2&(1<<j)) && (S2&(1<<(j+1)))) fl=0;
                if(!fl) {
                    if(!S2) break;
                    continue;
                }
                (dp[t][S2]+=dp[t-1][S1])%=P;
                if(!S2) break;
            }
        }
    }
    ll res=0;
    rep(i,0,A[t]) res+=dp[t][i];
    res%=P;
    return res;
}
 
 
 
int main(){
    n=rd();
    ll ans=1;
    rep(i,1,n) {
        if(i%2==0||i%3==0) continue;
        //这是一个联通块中最左上角的点
        ans=ans*Solve(i)%P;
    }
    printf("%lld\n",ans);
}
 
posted @ 2019-10-13 10:08  chasedeath  阅读(83)  评论(0编辑  收藏  举报