[luogu3290][SCOI2016]围棋

11 篇文章 0 订阅
1 篇文章 0 订阅

前言

一道dp题

题目相关

题目链接

题目大意

一个 n ∗ m n*m nm的棋盘(0,1,2)
并给出一个 2 ∗ c 2*c 2c的模板,求多少种棋盘包含模板
q q q次询问
答案模 1 e 9 + 7 1e9+7 1e9+7

数据范围

n ≤ 100 , m ≤ 12 , c ≤ 6 , q ≤ 5 n\le 100,m\le12,c\le6,q\le5 n100,m12,c6,q5

题解

首先我们发现包含模板的数量不好算,但是我们发现可以求出不包含模板的数量 k k k,这样 a n s = 3 n ∗ m − k ans=3^{n*m}-k ans=3nmk
考虑如何求 k k k
我们发现 3 12 = 531441 3^{12}=531441 312=531441,直接平方统计答案将会受不了,所以我们考虑不那么暴力的dp
我们发现有一种非常套路的dp方式,就是一个一个加,但我们算一下 m a x { n ∗ m ∗ 3 m } max\{n*m*3^m\} max{nm3m}仍然过于大了,我们需要改变一下存状态的方式
我们发现对于一个确定的模板,我们存状态的时候不需要存那个位置的具体的值,我们只要存那一行它是否能成为右端点就好了,我们发现 m a x { n ∗ m ∗ 2 m } = 4915200 max\{n*m*2^m\}=4915200 max{nm2m}=4915200明显可以接受
我们再考虑如何计算状态和计算答案,我们只需要再开两维做模板第一行、第二行分别的kmp的情况就好了,总复杂度为 O ( q n m 2 m − c c 2 ) \mathcal O(qnm2^{m-c}c^2) O(qnm2mcc2)
我们发现虽然说看起来复杂度很玄,但我们冷静分析,发现上界是 O ( q n m 2 m ) \mathcal O(qnm2^m) O(qnm2m),随便过

代码

#include<cstdio>
#include<cctype>
#include<algorithm>
#include<cstring>
//#include<ctime>
#define rg register
typedef long long ll;
template <typename T> inline T max(const T a,const T b){return a>b?a:b;}
template <typename T> inline T min(const T a,const T b){return a<b?a:b;}
template <typename T> inline void mind(T&a,const T b){a=a<b?a:b;}
template <typename T> inline void maxd(T&a,const T b){a=a>b?a:b;}
template <typename T> inline T abs(const T a){return a>0?a:-a;}
template <typename T> inline void Swap(T&a,T&b){T c=a;a=b;b=c;}
//template <typename T> inline void swap(T*a,T*b){T c=a;a=b;b=c;}
template <typename T> inline T gcd(const T a,const T b){if(!b)return a;return gcd(b,a%b);}
template <typename T> inline T lcm(const T a,const T b){return a/gcd(a,b)*b;}
template <typename T> inline T square(const T x){return x*x;};
template <typename T> inline void read(T&x)
{
	char cu=getchar();x=0;bool fla=0;
	while(!isdigit(cu)){if(cu=='-')fla=1;cu=getchar();}
	while(isdigit(cu))x=x*10+cu-'0',cu=getchar();
	if(fla)x=-x;
}
template <typename T> inline void printe(const T x)
{
	if(x>=10)printe(x/10);
	putchar(x%10+'0');
}
template <typename T> inline void print(const T x)
{
	if(x<0)putchar('-'),printe(-x);
	else printe(x);
}
const ll mod=1000000007;
ll pow(ll x,ll y)
{
	ll res=1;
	for(;y;y>>=1,x=x*x%mod)if(y&1)res=res*x%mod;
	return res;
}
int n,m,c,q;
int S;
int f[2][4096][7][7];
void clean(int x)
{
	for(rg int i=0;i<S;i++)
		for(rg int j=0;j<c;j++)
			for(rg int k=0;k<c;k++)
				f[x][i][j][k]=0;
}
ll ans;
struct kmp
{
	char b[7];int nxt[7];
	void KMP()
	{
		nxt[1]=0;
		int i,j=0;
		for(i=2;i<=c;i++)
		{
			while(j&&b[i]!=b[j+1])j=nxt[j];
			if(b[i]==b[j+1])j++;
			nxt[i]=j;
		}
	}
	bool KMPING(char B,int&j)
	{
		while(j&&B!=b[j+1])j=nxt[j];
		if(B==b[j+1])j++;
		if(j==c)
		{
			j=nxt[j];
			return 1;
		}
		return 0;
	}
}Q[2];
int main()
{
	read(n),read(m),read(c),read(q); 
	S=1<<(m-c+1);
	while(q--)
	{
		ans=pow(3,n*m);
		scanf("%s %s",Q[0].b+1,Q[1].b+1);
		Q[0].KMP(),Q[1].KMP();
		int las=1,dq=0;
		clean(0);
		f[0][0][0][0]=1;
		for(rg int i=1;i<=n;i++)
			for(rg int j=1;j<=m;j++)
			{
				dq^=1,las^=1;
				clean(dq);
				for(rg int k=0;k<S;k++)
					for(rg int a=0;a<c;a++)
						for(rg int b=0;b<c;b++)
						{
							const ll v=f[las][k][a][b];
							if(!v)continue;
							{//0
								int J0=a,J1=b;
								if(j==1)J0=J1=0;
								bool r0=Q[0].KMPING('W',J0),r1=Q[1].KMPING('W',J1);
								int nk=k,na=a,nb=b;
								if(j<c||!((nk&(1<<(j-c)))&&r1))
								{
									if(nk&(1<<(j-c)))nk^=1<<(j-c);
									if(r0)nk^=1<<(j-c);
									na=J0,nb=J1;
									(f[dq][nk][na][nb]+=v)%=mod;
								}
							}
							{//1
								int J0=a,J1=b;
								if(j==1)J0=J1=0;
								bool r0=Q[0].KMPING('B',J0),r1=Q[1].KMPING('B',J1);
								int nk=k,na=a,nb=b;
								if(j<c||!((nk&(1<<(j-c)))&&r1))
								{
									if(nk&(1<<(j-c)))nk^=1<<(j-c);
									if(r0)nk^=1<<(j-c);
									na=J0,nb=J1;
									(f[dq][nk][na][nb]+=v)%=mod;
								}
							}
							{//2
								int J0=a,J1=b;
								if(j==1)J0=J1=0;
								bool r0=Q[0].KMPING('X',J0),r1=Q[1].KMPING('X',J1);
								int nk=k,na=a,nb=b;
								if(j<c||!((nk&(1<<(j-c)))&&r1))
								{
									if(nk&(1<<(j-c)))nk^=1<<(j-c);
									if(r0)nk^=1<<(j-c);
									na=J0,nb=J1;
									(f[dq][nk][na][nb]+=v)%=mod;
								}
							}
						}
			}
		for(rg int k=0;k<S;k++)
			for(rg int a=0;a<c;a++)
				for(rg int b=0;b<c;b++)
					ans=ans+mod-f[dq][k][a][b];
		print(ans%mod),putchar('\n');
	}
	return 0;
}

总结

清新的dp题,练练手

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值