题解 P2595 【[ZJOI2009]多米诺骨牌】
这道题还是比较好想的,就是细节多了一些。
网上的题解大部分都是插头
首先考虑没有限制的情况,即,用
考虑按照
的顺序加点。加入
但是
更进一步,我们考虑对所有网格图的所有子矩阵求出没有限制的答案(之后有用)。设左上角为
现在考虑有限制的情况。想到容斥。比较流行的容斥有两种:
其中
这样,求出每个子集的
时间复杂度方面,一开始的预处理主要瓶颈在状压。它是指数级别的。最坏情况下是
后面的统计,子集容斥一次
总时间复杂度是
很多人说我题解写得太详细是在浪费时间。用某学姐的话说,“好像在谈恋爱”。但我想,题解不仅是个人的总结,更是对其他人的帮助。这道题网络上有很多题解,但大多数都是三言两语就带过,很难看懂。不仅这道题,很多题也是一样。而我所做的,就是将我的理解,通过通俗易懂的方式,呈现给大家。能用短暂的两三年
代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<math.h>
using namespace std;
const int N=15;
const int T=1<<N;
const int mod=19901013;
int n,m,pre[N][N][N][N],tdp[2][T],ans;
int f[N],tl,cnt[T];
pair<int,int> lis[N];
char s[N][N];
namespace MATHEMATICS
{
int add(int x,int y)
{
int ret=x+y;
if(ret>=mod) ret-=mod;
return ret;
}
int mi(int x,int y)
{
int ret=x-y;
if(ret<0) ret+=mod;
return ret;
}
void inc(int &x,int y)
{
x+=y;
if(x>=mod) x-=mod;
}
void dec(int &x,int y)
{
x-=y;
if(x<0) x+=mod;
}
int mul(int x,int y)
{
return 1LL*x*y%mod;
}
}
using namespace MATHEMATICS;
void init()
{
int i,j;
scanf("%d%d",&n,&m);
for(i=0;i<n;++i) scanf("%s",&s[i]);
}
void cal(int xu,int yu,int xd)
{
int i,j,S,mxS=1<<xd-xu+1;
int oldpos=0,newpos=1;
for(S=0;S<mxS;++S) tdp[0][S]=0;
tdp[0][mxS-1]=1;
for(i=yu;i<n;++i)
for(j=xu;j<=xd;++j)
{
for(S=0;S<mxS;++S) tdp[newpos][S]=0;
for(S=0;S<mxS;++S)
{
if(!tdp[oldpos][S]) continue;
if(!(S&(1<<xd-xu))&&s[i][j]=='.'&&s[i-1][j]=='.')
inc(tdp[newpos][(S<<1)&(mxS-1)|1],tdp[oldpos][S]);
if(!(S&1)&&s[i][j]=='.'&&j>xu&&s[i][j-1]=='.')
inc(tdp[newpos][(S<<1)&(mxS-1)|3],tdp[oldpos][S]);
inc(tdp[newpos][(S<<1)&(mxS-1)],tdp[oldpos][S]);
}
if(j==xd)
{
for(S=0;S<mxS;++S)
inc(pre[xu][yu][xd][i],tdp[newpos][S]);
}
oldpos^=1,newpos^=1;
}
}
void prework()
{
int i,j,k;
for(i=0;i<m;++i)
for(j=0;j<n;++j)
for(k=i;k<m;++k)
cal(i,j,k);
}
int calc(int S)
{
int i,j,k,las=0;tl=0;
for(i=0;i<m-1;++i)
if((1<<i)&S)
lis[++tl]=make_pair(las,i),las=i+1;
lis[++tl]=make_pair(las,m-1);
for(i=0;i<n;++i)
{
f[i]=1;
for(j=1;j<=tl;++j)
f[i]=mul(f[i],pre[lis[j].first][0][lis[j].second][i]);
for(k=0;k<i;++k)
{
int now=f[k];
for(j=1;j<=tl;++j)
now=mul(now,pre[lis[j].first][k+1][lis[j].second][i]);
dec(f[i],now);
}
}
return f[n-1];
}
void work()
{
int ans=0,S,mxS=1<<m-1;
for(S=0;S<mxS;++S) cnt[S]=cnt[S>>1]+(S&1);
for(S=0;S<mxS;++S)
{
if(cnt[S]&1) dec(ans,calc(S));
else inc(ans,calc(S));
}
printf("%d\n",ans);
}
int main()
{
init();prework();work();
return 0;
}