前言
一道dp题
题目相关
题目大意
一个
n
∗
m
n*m
n∗m的棋盘(0,1,2)
并给出一个
2
∗
c
2*c
2∗c的模板,求多少种棋盘包含模板
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 n≤100,m≤12,c≤6,q≤5
题解
首先我们发现包含模板的数量不好算,但是我们发现可以求出不包含模板的数量
k
k
k,这样
a
n
s
=
3
n
∗
m
−
k
ans=3^{n*m}-k
ans=3n∗m−k
考虑如何求
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{n∗m∗3m}仍然过于大了,我们需要改变一下存状态的方式
我们发现对于一个确定的模板,我们存状态的时候不需要存那个位置的具体的值,我们只要存那一行它是否能成为右端点就好了,我们发现
m
a
x
{
n
∗
m
∗
2
m
}
=
4915200
max\{n*m*2^m\}=4915200
max{n∗m∗2m}=4915200明显可以接受
我们再考虑如何计算状态和计算答案,我们只需要再开两维做模板第一行、第二行分别的kmp的情况就好了,总复杂度为
O
(
q
n
m
2
m
−
c
c
2
)
\mathcal O(qnm2^{m-c}c^2)
O(qnm2m−cc2)
我们发现虽然说看起来复杂度很玄,但我们冷静分析,发现上界是
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题,练练手