「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\n",(ans+mod)%mod);
    return 0;
}
上一篇:P2055 [ZJOI2009]假期的宿舍 二分图匹配


下一篇:P2598 [ZJOI2009]狼和羊的故事 最小割