「SCOI2016」围棋 解题报告

「SCOI2016」围棋

打CF后困不拉基的,搞了一上午...

考虑直接状压棋子,然后发现会t

考虑我们需要上一行的状态本质上是某个位置为末尾是否可以匹配第一行的串

于是状态可以\(2^m\)压住了,但还是会T

考虑到复杂度瓶颈在于每行的状态都要枚举上一行的状态,是按行转移的。

那么如果做一个轮廓线,就可以按格子转移

考虑有那些状态,当前格子\(i,j\),当前轮廓线是否可以匹配第一行的串的状态\(s\)

然后你试试发现如果想好好转移

得存一个\((i,j)\)匹配到第一行串的位置\(x\),和第二行串的位置\(y\)

这里的思考方向倾向于先想转移,转移不了再加状态

然后发现这个匹配的过程可以使用KMP优化,转移的均摊是\(O(1)\)

复杂度\(O(nm2^{m-c+1}c^2)\)

这里复杂度写成\(2^m\)可能不太稳...


Code:

#include <cstdio>
#include <cctype>
#include <cstring>
template <class T>
void read(T &x)
{
	x=0;char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)) x=x*10+c-'0',c=getchar();
}
const int mod=1e9+7;
void add(int &x,int y){x+=y;if(x>=mod)x-=mod;}
void mul(int &x,int y){x=1ll*x*y%mod;}
int n,m,c,q,dp[2][1<<12][7][7];
int nxt1[12],nxt2[12],d1[12],d2[12];
char s1[12],s2[12];
void init(char *s,int *d,int *nxt)
{
	for(int i=1;i<=c;i++) d[i]=(s[i]=='W'?2:(s[i]=='B'));
	for(int j=0,i=2;i<=c;i++)
	{
		while(j&&d[j+1]!=d[i]) j=nxt[j];
		if(d[j+1]==d[i]) ++j;
		nxt[i]=j;
	}
}
int get(int *nxt,int p,int col,int *d)
{
	while(p&&d[p+1]!=col) p=nxt[p];
	if(d[p+1]==col) ++p;
	return p;
}
int main()
{
	read(n),read(m),read(c),read(q);
	while(q--)
	{
	    memset(d1,-1,sizeof d1),memset(nxt1,0,sizeof nxt1);
	    memset(d2,-1,sizeof d2),memset(nxt2,0,sizeof nxt2);
		scanf("%s",s1+1),init(s1,d1,nxt1);
		scanf("%s",s2+1),init(s2,d2,nxt2);
		memset(dp,0,sizeof dp);
		int sum=1,cur=0,tx,ty,t,l=m-c+1;
		dp[cur][0][0][0]=1;
		for(int i=1;i<=n;i++)
		{
			for(int j=0;j<m;j++)
			{
				memset(dp[cur^1],0,sizeof dp[cur^1]);
				for(int s=0;s<1<<l;s++)
					for(int x=0;x<=c;x++)
						for(int y=0;y<=c;y++)
							if(dp[cur][s][x][y])
								for(int col=0;col<3;col++)
								{
									tx=get(nxt1,x,col,d1);
									ty=get(nxt2,y,col,d2);
									if(j+1>=c)
                                    {
                                        t=s&(~(1<<j+1-c));
                                        t|=(tx==c)<<j+1-c;
                                    }
									else t=s;
									if(j+1>=c&&(s>>j+1-c&1)&&ty==c) continue;
									add(dp[cur^1][t][tx][ty],dp[cur][s][x][y]);
								}
				cur^=1;
				mul(sum,3);
			}
			for(int s=0;s<1<<l;s++)
				for(int x=0;x<=c;x++)
					for(int y=0;y<=c;y++)
                        if(x||y)
                            add(dp[cur][s][0][0],dp[cur][s][x][y]),dp[cur][s][x][y]=0;
		}
		int ans=0;
		for(int s=0;s<1<<l;s++)
			add(ans,dp[cur][s][0][0]);
		add(sum,mod-ans);
		printf("%d\n",sum);
	}
	return 0;
}

2019.3.6

posted @ 2019-03-06 11:59  露迭月  阅读(467)  评论(0编辑  收藏  举报