bzoj4572: [Scoi2016]围棋(轮廓线dp)

传送门
题解可以参见 z j j zjj zjj神仙的,写的很清楚。
代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
typedef long long ll;
const int mod=1e9+7;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline void update(int&a,const int&b){a=add(a,b);}
inline void modify(int&a,const int&b){a=dec(a,b);}
int ans,f[2][1<<12|5][13][13],n,m,c,q,a[13],fail[2][13],trans[2][13][3],tmp;
char s[13];
inline int idx(const char&x){return x=='W'?0:(x=='B'?1:2);}
int main(){
	scanf("%d%d%d%d",&n,&m,&c,&q);
	while(q--){
		for(ri tt=0;tt<2;++tt){
			scanf("%s",s+1);
			for(ri i=1;i<=c;++i)a[i]=idx(s[i]);
			for(ri i=1,j=0;i<=c;++i){
				while(j&&s[i+1]!=s[j+1])j=fail[tt][j];
				s[i+1]==s[j+1]?fail[tt][i+1]=++j:fail[tt][i+1]=0;
			}
			for(ri k,i=0;i<c;++i)for(ri j=0;j<3;++j){
				for(k=i;k&&a[k+1]!=j;k=fail[tt][k]);
				trans[tt][i][j]=a[k+1]==j?k+1:0;
			}
		}
		memset(f[0],0,sizeof(f[0]));
		ans=f[0][0][0][0]=1,tmp=1;
		for(ri i=1;i<=n;++i){
			memset(f[tmp],0,sizeof(f[tmp]));
			for(ri j=0,up=1<<(m-c+1);j<up;++j)for(ri k=0;k<c;++k)for(ri l=0;l<c;++l)update(f[tmp][j][0][0],f[tmp^1][j][k][l]);
			tmp^=1;
			for(ri j=1;j<=m;++j,tmp^=1){
				ans=mul(ans,3);
				memset(f[tmp],0,sizeof(f[tmp]));
				for(ri sta=0,up=1<<(m-c+1);sta<up;++sta)for(ri k=0;k<c;++k)for(ri l=0;l<c;++l){
					if(!f[tmp^1][sta][k][l])continue;
					for(ri a,b,ns,t=0;t<3;++t){
						a=trans[0][k][t],b=trans[1][l][t],ns=sta;
						if(j>=c&&((sta>>(j-c))&1))ns^=1<<(j-c);
						if(a==c)ns^=1<<(j-c),a=fail[0][a];
						if(b==c){if((sta>>(j-c))&1)continue;b=fail[1][b];}
						update(f[tmp][ns][a][b],f[tmp^1][sta][k][l]);
					}
				}
			}
		}
		tmp^=1;
		for(ri i=0,up=1<<(m-c+1);i<up;++i)for(ri j=0;j<c;++j)for(ri k=0;k<c;++k)modify(ans,f[tmp][i][j][k]);
		cout<<ans<<'\n';
	}
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值