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

$$-----EOF-----$$

posted @ 2022-05-02 20:35  AlienCollapsar  阅读(37)  评论(1编辑  收藏  举报
// 生成目录索引列表 // ref: http://www.cnblogs.com/wangqiguo/p/4355032.html // modified by: zzq