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