「SCOI2011」地板 题解 (插头DP)
题目简介
用任意 \(L\) 型的地板砖铺 \(n\times m\) 的地面,有的地方不能铺,求方案总数。
分析
蒟蒻认为最接近模板的插头DP
step 1.
倘若 \(n=1\) 或者 \(m = 1\) ,那么答案即为 \(0\) ,本处不予讨论。
排除这种极端情况,由 \(n\times m \leq 100\) 可知,\(\min(n,m)<= 10\)。
将 \(n\) 定义为原 \(n,m\) 中较大的那个,\(m\) 定义为原 \(n,m\) 中较小的那个。
step 2.
对于插头状态进行压缩:
- \(0\) 表示当前位置无插头
- \(1\) 表示当前位置的插头还未经过 \(L\) 型拐角
- \(2\) 表示当前位置的插头已经过(或正在经过)\(L\) 型拐角
step 3.
逐格递推,分类讨论:
-
situation 1:左边和上边均无插头
- solution 1:向右增加一个 \(2\) 插头,向下增加一个 \(2\) 插头,作为新一块地板砖的拐角。
- solution 2:向右增加一个 \(1\) 插头,作为新一块地板砖的起点。
- solution 3:向下增加一个 \(1\) 插头,作为新一块地板砖的起点。
-
situation 2:左边无插头,上边有一个 \(1\) 插头
- solution 1:向下增加一个 \(1\) 插头,进行延伸。
- solution 2:向右增加一个 \(2\) 插头,进行拐弯。
-
situation 3:左边有一个 \(1\) 插头,上边无插头(与 situation 2 相似)
- solution 1:向右增加一个 \(1\) 插头,进行延伸。
- solution 2:向下增加一个 \(2\) 插头,进行拐弯。
-
situation 4:左边无插头,上边有一个 \(2\) 插头
- solution 1:向下增加一个 \(2\) 插头,进行延伸。
- solution 2:不接出插头,作为上一块地板砖的终点。
-
situation 5:左边有一个 \(2\) 插头,上边无插头(与 situation 4 相似)
- solution 1:向右增加一个 \(2\) 插头,进行延伸。
- solution 2:不接出插头,作为上一块地板砖的终点。
-
situation 6:左边和上边均有一个 \(1\) 插头
- solution:不接出插头,作为上一块地板砖的终点。
注意:对于situation 4、5、6,倘若当前格子为最后一个有效格子,需要累加答案。
其余的状态均为非法,不可能出现。
\(AC\ Code\)
#include<cstdio>
#include<iostream>
#include<unordered_map>
using namespace std;
typedef long long ll;
const int Maxn=2e5+5;
const int Mod=20110520;
int a[105][105];
int edx,edy;
ll inc[105];
int cnt[2],cur;
int sta[2][Maxn];
ll val[2][Maxn];
unordered_map<int,int>u;
inline void add(int p,ll x){
if(!u.count(p)){
u[p]=++cnt[cur];
sta[cur][cnt[cur]]=p;
val[cur][cnt[cur]]=0;
}
(val[cur][u[p]]+=x)%=Mod;
}
ll solve(int n,int m){
ll ans=0;
cnt[cur]=1;
sta[cur][1]=0;
val[cur][1]=1;
for(int i=1;i<=n;++i){
for(int j=1;j<=cnt[cur];++j)sta[cur][j]<<=2;
for(int j=1;j<=m;++j){
int las=cur;
u.clear();cnt[cur^=1]=0;
for(int k=1;k<=cnt[las];++k){
int bit=sta[las][k];
ll num=val[las][k];
int p=(bit>>(j-1<<1))%4,q=(bit>>(j<<1))%4;
if(!a[i][j]){
if(!p&&!q)add(bit,num);
}else if(!p&&!q){
if(a[i][j+1]&&a[i+1][j])
add(bit+(inc[j-1]+inc[j]<<1),num);
if(a[i][j+1])add(bit+inc[j],num);
if(a[i+1][j])add(bit+inc[j-1],num);
}else if(!p&&q==1){
if(a[i][j+1])add(bit+inc[j],num);
if(a[i+1][j])add(bit+inc[j-1]-inc[j],num);
}else if(p==1&&!q){
if(a[i+1][j])add(bit+inc[j-1],num);
if(a[i][j+1])add(bit-inc[j-1]+inc[j],num);
}else if(!p&&q==2){
if(a[i+1][j])add(bit+(inc[j-1]-inc[j]<<1),num);
add(bit-(inc[j]<<1),num);
if(i==edx&&j==edy)(ans+=num)%=Mod;
}else if(p==2&&!q){
if(a[i][j+1])add(bit+(inc[j]-inc[j-1]<<1),num);
add(bit-(inc[j-1]<<1),num);
if(i==edx&&j==edy)(ans+=num)%=Mod;
}else if(p==1&&q==1){
add(bit-inc[j-1]-inc[j],num);
if(i==edx&&j==edy)(ans+=num)%=Mod;
}
}
}
}
return ans;
}
char s[105];
int main(){
int n,m;cin>>n>>m;
for(int i=1;i<=n;++i){
cin>>s+1;
for(int j=1;j<=m;++j){
int x=i,y=j;if(n<m)swap(x,y);
a[x][y]=s[j]=='_';
if(a[x][y])edx=x,edy=y;
}
}
if(n<m)swap(n,m);
inc[0]=1;
for(int i=1;i<=m;++i)inc[i]=inc[i-1]<<2;
cout<<solve(n,m)<<'\n';
return 0;
}