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
#include
#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<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

你可能感兴趣的:(Luogu P3290 [SCOI2016]围棋)