zoukankan      html  css  js  c++  java
  • 「ZJOI2009」多米诺骨牌

    「ZJOI2009」多米诺骨牌

    要求满足任何相邻两行之间都有至少一个 骨牌横跨,任何相邻两列之间也都至少有一个骨牌横跨。

    枚举有哪些列之间是没有骨牌横跨的,算出该情况下合法的方案数,容斥。

    确定了哪些列是没有骨牌横跨的,列就被划分成了几个区间。

    计算前(i)个区间的 任意两行之间有骨牌的 方案。

    对于当前的(i),合法的方案为 任意摆放的方案,再依次减去:对于行 ([1,i-1]) ,该行以及之前的行 任意两行之间有骨牌,该行之后的行 随意摆放的方案数。(余下的都是合法的方案)

    实际上,这里需要知道某一个二维区间任意摆放的方案数(因为有不能摆放的位置),需要预处理。

    这里,处理出 ([lk,rk])行 任意摆的方案数复杂度(o(n^3)),而 计算总的方案复杂度(o(n^2))

    该部分的复杂度为(o(2^n*n^3)),当然,复杂度不满。

    现在求左上端点([l,r]),右下端点([l1,r1])的区间任意摆放的方案。

    考虑枚举(r,r1,l),求出(l1 in [l,n])的答案。

    之后就是一个轮廓线dp了,转移的时候枚举当前点的覆盖情形即可,一次的复杂度为(o(2^n*n^2 ))

    复杂度(o(2^n*n^5))...但实际上(n=15,m=15)的情况下只有大约有2e8的计算。

    在最终测试的时候表现十分的优秀。

    #include<bits/stdc++.h>
    #define rep(q,a,b) for(int q=a,q##_end_=b;q<=q##_end_;++q)
    #define dep(q,a,b) for(int q=a,q##_end_=b;q>=q##_end_;--q)
    #define mem(a,b) memset(a,b,sizeof a )
    #define debug(a) cerr<<#a<<' '<<a<<"___"<<endl
    using namespace std;
    void in(int &r) {
        static char c;
        r=0;
        while(c=getchar(),!isdigit(c));
        do r=(r<<1)+(r<<3)+(c^48);
        while(c=getchar(),isdigit(c));
    }
    const int mod=19901013;
    char as[20][20];
    int val[16][16][16][16];
    int dp[2][(1<<15)+5];
    void add(int &a,int b){
        a=(a+b)%mod;
    }
    int n,m;
    void get(int l,int r,int st){
        bool cur=0;
        int td=(1<<r-l+1)-1;
        mem(dp,0);
        rep(tot,0,td)dp[cur][tot]=0;
        dp[cur][0]=1;
        rep(q,st,n){
            char *nw=as[q],*ne=as[q+1];
            rep(w,0,r-l){
                cur^=1;
                int *x=dp[!cur];
                int *y=dp[cur];
                rep(tot,0,td)y[tot]=0;
                rep(tot,0,td)if(x[tot]){
                    int v=x[tot];
                    if((1<<w)&tot)add(y[tot^(1<<w)],v);
                    else{
                        if(nw[w+l]=='x')goto st;
                        if(w!=r-l&&nw[w+l+1]=='.'&&!(tot&(1<<w+1)))add(y[tot|(1<<w+1)],v);
                        if(ne[w+l]=='.')add(y[tot|(1<<w)],v);
    st:
                        add(y[tot],v);
                    }
                }
            }
            val[st][l][q][r]=dp[cur][0];
        }
    }
    void init(){
        rep(q,1,m)rep(w,q,m)rep(e,1,n)get(q,w,e);
    }
    int ty[(1<<15)+5];
    struct node{
        int l,r;
    }an[16];
    int mul[20][20],sum[20];
    int get(int mk){
        int ct=0,last=1;
        rep(q,0,m-1)if(mk&(1<<q)){
            an[++ct]={last,q+1};
            last=q+2;
        }
        if(last<=m)an[++ct]={last,m};
        rep(q,1,n)rep(w,q,n){
            int mv=1;
            rep(e,1,ct)mv=1LL*mv*val[q][an[e].l][w][an[e].r]%mod;
            mul[q][w]=mv;
        }
        rep(q,1,n){
            sum[q]=mul[1][q];
            rep(w,1,q-1)sum[q]=(sum[q]-1LL*sum[w]*mul[w+1][q])%mod;
        }
        return sum[n];
    }
    int main(){
        in(n),in(m);
        rep(q,1,n)scanf("%s",as[q]+1);
        init();
        ty[0]=1;
        rep(q,1,(1<<m)-1)ty[q]=-1*ty[q&q-1];
        int ans=0;
        rep(q,0,(1<<m-1)-1)ans=(ans+ty[q]*get(q))%mod;
        printf("%d
    ",(ans+mod)%mod);
        return 0;
    }
    
  • 相关阅读:
    【操作系统】 管程机制
    【Java】 大话数据结构(13) 查找算法(4) (散列表(哈希表))
    【操作系统】 信号量机制
    【Java】 奇偶数的判断
    【Java】 大话数据结构(12) 查找算法(3) (平衡二叉树(AVL树))
    MySQL之库操作
    数据库简介
    MySQL的知识海洋
    python并发编程之多进程(理论)
    python并发编程之多进程(实现)
  • 原文地址:https://www.cnblogs.com/klauralee/p/10855997.html
Copyright © 2011-2022 走看看