BZOJ2331:[SCOI2011]地板——题解
http://www.lydsy.com/JudgeOnline/problem.php?id=2331
题面复制于洛谷
题目描述
lxhgww的小名叫”小L“,这是因为他总是很喜欢L型的东西。小L家的客厅是一个R*C的矩形,现在他想用L型的地板来铺满整个客厅,客厅里有些位置有柱子,不能铺地板。现在小L想知道,用L型的地板铺满整个客厅有多少种不同的方案?需要注意的是,如下图所示,L型地板的两端长度可以任意变化,但不能长度为0。
铺设完成后,客厅里面所有没有柱子的地方都必须铺上地板,但同一个地方不能被铺多次。
输入输出格式
输入格式:
输入的第一行包含两个整数,R和C,表示客厅的大小。接着是R行,每行C个字符。'_'表示对应的位置是空的,必须铺地板;'*'表示对应的位置有柱子,不能铺地板。
输出格式:
输出一行,包含一个整数,表示铺满整个客厅的方案数。由于这个数可能很大,只需输出它除以20110520的余数。
输入输出样例
参考了:http://blog.csdn.net/regina8023/article/details/44838887
我终于可以告别插头dp啦233333……<—此人已疯
这道题的难点在于将插头dp的插头的定义进行修改。
0:无插头
1:有插头且当前格子所在的地板能再转弯。
2:有插头且当前格子所在的地板不能再转弯。
有了这些就可以按照插头dp的思想进行分情况讨论了:
(摘自参考博客)
1.00-->22 或 10 或 01
2.11-->00
3.10-->20 或 01
20-->00 或 02
4.01-->10 或 02
02-->00 或 20
最终把所有情况枚举累加即可。
PS:第二种情况的11转换成了00实质上是11相交的地方变成了这块地板的转折点(也可以理解为两块地板并在了一起)。
#include<algorithm> #include<cstdio> #include<cstring> using namespace std; typedef long long ll; const int INF=2147483647; const int mod=300000; const int M=1000005; const int p=20110520; struct node{ int to,nxt; }edge[M]; int head[M],cnt; int n,m; bool mp[105][105]; int cur,pre; int state[2][M]; ll ans[2][M],cntt; int tot[2]; int bit[15]; inline void getbit(){ for(int i=1;i<15;i++)bit[i]=i<<1; return; } inline void add(int u,int v){ cnt++; edge[cnt].to=v; edge[cnt].nxt=head[u]; head[u]=cnt; return; } void insert(int now,ll num){ int u=now%mod; for(int i=head[u];i;i=edge[i].nxt){ int v=edge[i].to; if(state[cur][v]==now){ ans[cur][v]+=num%p; ans[cur][v]%=p; return; } } add(u,++tot[cur]); state[cur][tot[cur]]=now; ans[cur][tot[cur]]=num%p; return; } void plugdp(){ cur=0; tot[cur]=1; ans[cur][1]=1; state[cur][1]=0; for(int i=1;i<=n;i++){ for(int j=1;j<=tot[cur];j++){ state[cur][j]<<=2; } for(int j=1;j<=m;j++){ memset(head,0,sizeof(head));cnt=0; pre=cur,cur^=1; tot[cur]=0; for(int k=1;k<=tot[pre];k++){ int now=state[pre][k]; ll num=ans[pre][k]%p; int is_down=(now>>bit[j-1])%4; int is_right=(now>>bit[j])%4; if(!mp[i][j]){ if(!is_down&&!is_right) insert(now,num); } else if(!is_down&&!is_right){ if(mp[i+1][j]) insert(now+(1<<bit[j-1]),num); if(mp[i][j+1]) insert(now+(1<<bit[j]),num); if(mp[i][j+1]&&mp[i+1][j]) insert(now+2*(1<<bit[j-1])+2*(1<<bit[j]),num); } else if(is_down&&!is_right){ if(is_down==1){ if(mp[i+1][j])insert(now+(1<<bit[j-1]),num); if(mp[i][j+1])insert(now-(1<<bit[j-1])+(1<<bit[j]),num); }else{ insert(now-2*(1<<bit[j-1]),num); if(mp[i][j+1])insert(now-2*(1<<bit[j-1])+2*(1<<bit[j]),num); } } else if(!is_down&&is_right){ if(is_right==1){ if(mp[i+1][j])insert(now+(1<<bit[j-1])-(1<<bit[j]),num); if(mp[i][j+1])insert(now+(1<<bit[j]),num); }else{ insert(now-2*(1<<bit[j]),num); if(mp[i+1][j])insert(now+2*(1<<bit[j-1])-2*(1<<bit[j]),num); } } else if(is_down==1&&is_right==1) insert(now-(1<<bit[j-1])-(1<<bit[j]),num); } } } for(int k=1;k<=tot[cur];k++)cntt+=ans[cur][k]; return; } int main(){ getbit(); scanf("%d%d",&n,&m); if(n<m){ swap(n,m); for(int i=1;i<=m;i++){ for(int j=1;j<=n;j++){ char ch=getchar(); while(ch!='*'&&ch!='_')ch=getchar(); if(ch=='_')mp[j][i]=1; } } }else{ for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ char ch=getchar(); while(ch!='*'&&ch!='_')ch=getchar(); if(ch=='_')mp[i][j]=1; } } } plugdp(); printf("%lld\n",cntt); return 0; }