Luogu P3290 [SCOI2016]围棋

ORZ陈指导花式切题,奶一口今年陈指导必进队

虽然这题是插头DP的练习题,但是数据太水我们仍然可以直接状压水过去

首先考虑我们只需要求出一个都不匹配的方案数然后减一下就好了,所以我们考虑怎么算不匹配的方案数

相信大家都注意到了只有两行这个条件,再加上数据范围很小,我们先考虑一行的情况

\(g_{0/1,s}\)表示\(s\)(三进制状压表示这一行怎么填)和模板串第\(0/1\)行匹配的情况(二进制状压是否成功)

然后记\(f_{i,s}\)表示多行的情况,考虑每次转移两行不能有同位置的\(1\)对上,因此方程为:

\[f_{i,s}=\sum_{t\cap s=\emptyset} f_{i-1,t}\Leftrightarrow f_{i,s}=\sum_{t\in \complement s} f_{i-1,t} \]

然后我们注意到那个子集卷积显然可以用FWT或卷积优化,于是就做完了

复杂度\(O(n\times q\times 3^m)\),有点卡但是还是能过

#include<cstdio>
#include<cstring>
#define RI int
#define CI const int&
using namespace std;
const int N=531441,mod=1e9+7;
int n,m,c,q,lim,g[2][N],f[105][4096]; char t[15],s[2][10];
inline void inc(int& x,CI y)
{
	if ((x+=y)>=mod) x-=mod;
}
inline int quick_pow(int x,int p,int mul=1)
{
	for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
}
inline int comp(CI id,int ret=0)
{
	for (RI i=1,j;i+c-1<=m;++i)
	{
		bool flag=1; for (j=1;j<=c;++j)
		if (t[i+j-1]!=s[id][j]) { flag=0; break; }
		ret|=(flag<<i-1);
	}
	return ret;
}
inline void init(CI id,CI nw=1,CI s=0)
{
	if (nw>m) return (void)(g[id][s]=comp(id));
	t[nw]='W'; init(id,nw+1,s*3);
	t[nw]='B'; init(id,nw+1,s*3+1);
	t[nw]='X'; init(id,nw+1,s*3+2);
}
inline void FWT(int* f)
{
	for (RI i=1,j,k;i<lim;i<<=1) for (j=0;j<lim;j+=(i<<1))
	for (k=0;k<i;++k) inc(f[i+j+k],f[j+k]);
}
int main()
{
	for (scanf("%d%d%d%d",&n,&m,&c,&q);q;--q)
	{
		RI i,j; scanf("%s%s",s[0]+1,s[1]+1); lim=1<<m; int tot=quick_pow(3,m);
		for (init(0),init(1),memset(f[1],0,sizeof(f[1])),i=0;i<tot;++i)
		++f[1][g[0][i]]; for (FWT(f[1]),i=2;i<=n;FWT(f[i]),++i)
		for (memset(f[i],0,sizeof(f[i])),j=0;j<tot;++j)
		inc(f[i][g[0][j]],f[i-1][(lim-1)^g[1][j]]);
		printf("%d\n",(quick_pow(3,n*m)-f[n][lim-1]+mod)%mod);
	}
	return 0;
}
posted @ 2020-06-18 21:43  空気力学の詩  阅读(152)  评论(0编辑  收藏  举报