bzoj4572

一个鬼畜的做法...(估计没人看,看了也没人写..)(最终优化形态请看最后一个代码)

首先状压Dp应该都知道吧(然而yjq直接容斥强势艹过...),那么我们来优化状态,首先考虑不可能匹配成功的状态,把它们缩到一个状态,我们发现,哇!一下子少了好多状态!大概从几百万变成了5w-15w左右,但是Dp是状态数^2的,这样子肯定不行...然后一个鬼畜的优化就来了,我把与第一行匹配开始点相同的状态缩成一个状态(比如两个状态i和j,如果它
们和模板串都是从{1,5,6}开始可以匹配,那么其实它们可以当做一个状态,但是注意又可以匹配模板第一行,又可以匹配模板第二行的状态,那么它们和两行匹配的位置都必须相同.),然后发现有一维的状态变成了100+的数量,然后省选就90了,然后bzoj就卡着时限过了....估计用AC自动机进行中途的匹配过程可以快10倍左右吧(然而我这代码已经7k了,再多一点就....)...(还有一个脑洞不知道行不行,因为当时考试没足够时间,现在又与模板第一行匹配的状态k个,与第二行匹配的状态c个,之前考场上的做法只缩了k,但是c好像也可以缩一缩?要是c也能缩那估计就可以虐标算了.)

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <string>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <ctime>
#define Mod 1000000007
using namespace std;
struct node {int op;int val;int num;int k;int pos[20];int id;int pox[20];
};node Dp[600010];
struct edge{int next;int to;
};edge bian[10000010];
int first[600010],size = 0,cnt = 0,m,c,A[3][20],s[100];
int top = 0,v[100],pos[600010],queue[600010],tot = 0,n;
int p,Ans,f[110][10010][2],P[20],Q[20];
bool exist[600010],visit[600010];
void inser(int x,int y) {
	size ++;
	bian[size].to = y;
	bian[size].next = first[x];
	first[x] = size;
}
void check1(int x) {
	cnt = 0;
	for(int i = 0;i <= m - c;i ++)
	{
		bool F = true;
		for(int j = 1;j <= c;j ++)
			if(A[1][j] != s[i + j - 1])
				{F = false;break;}
		if(F == true) v[ ++cnt] = i;
	}
	if(cnt == 0) return ;
	exist[x] = true;
	top ++;
	Dp[top].k = 0;
	Dp[top].num = 1;
	Dp[top].val = x;
	Dp[top].op = cnt;
	for(int i = 1;i <= Dp[top].op;i ++)
		Dp[top].pos[i] = v[i];
	if(visit[x] == true) 
	{
		cnt = 0;
		for(int i = 0;i <= m -c;i ++)
		{
			bool F = true;
			for(int j = 1;j <= c;j ++)
				if(A[2][j] != s[i + j - 1])
					{F = false;break;}
			if(F) v[ ++cnt] = i;
		}
		Dp[top].id = cnt;
		for(int i = 1;i <= Dp[top].id;i ++)
			Dp[top].pox[i] = v[i];
	}
	else Dp[top].id = 0;
}
void check2(int x) {
	for(int i = 0;i <= m - c;i ++) 
	{
		bool F = true;
		for(int j = 1;j <= c;j ++)
			if(A[2][j] != s[i + j - 1])
				{F = false;break;}
		if(F == true) {visit[x] = true;queue[ ++tot] = x;return ;}
	}
}
bool check(int x,int y) {
	//cerr<<"HH"<<endl;
	for(int i = 0;i <= m - 1;i ++)
		P[i] = x % 3,x /= 3;
	for(int i = 0;i <= m - 1;i ++)
		Q[i] = y % 3,y /= 3;
	for(int i = 0;i <= m - c;i ++)
	{
		bool F = true;
		for(int j = 1;j <= c;j ++)
			if(A[1][j] != P[i + j - 1] || A[2][j] != Q[i + j - 1])
				 {F = false;break;}
		if(F) return true;
	}
	return false;
}
bool comp(const node &x,const node &y) {
	if(x.op != y.op) return x.op < y.op;
	if(x.id != y.id) return x.id < y.id;
	for(int i = 1;i <= x.op;i ++)
		if(x.pos[i] != y.pos[i])
			return x.pos[i] < y.pos[i];
	for(int i = 1;i <= x.id;i ++)
		if(x.pox[i] != y.pox[i])
			return x.pox[i] < y.pox[i];
	return x.val < y.val;
}
bool same(node A,node B) {
	if(A.op != B.op) return false;
	if(A.id != B.id) return false;
	for(int i = 1;i <= A.op;i ++)
		if(A.pos[i] != B.pos[i])
			return false;
	for(int i = 1;i <= A.id;i ++)
		if(A.pox[i] != B.pox[i])
			return false;
	return true;
}
bool what(const int &x,const int &y) {
	return pos[x] < pos[y];
}
int main() {
	freopen("alphago.in","r",stdin);
	freopen("alphago.out","w",stdout);
	scanf("%d%d%d%d",&n,&m,&c,&p);
	while(p --) 
	{
		Ans = 0;size = 0;top = 0;tot = 0;
		memset(exist,false,sizeof(exist));
		memset(visit,false,sizeof(visit));
		memset(first,0,sizeof(first));
		memset(queue,0,sizeof(queue));
		memset(pos,0,sizeof(pos));
		memset(Dp,0,sizeof(Dp));
		for(int i = 1;i <= 2;i ++)
		for(int j = 1;j <= c;j ++) 
		{
			char c = 'd';
			while(c != 'B' && c != 'W' && c != 'X')	
				c = getchar();
			if(c == 'B') A[i][j] = 1;
			if(c == 'W') A[i][j] = 2;
			if(c == 'X') A[i][j] = 0;
		}
		int N = 1;
		for(int i = 1;i <= m;i ++)
			N = N * 3;
		for(int i = 0;i <= N - 1;i ++) 
		{
			int M = i;
			for(int j = 0;j <= m - 1;j ++) 
			{
				s[j] = M % 3;
				M /= 3;
			}
			check2(i);
			check1(i);
		}
		int O = N - top;
		sort(Dp + 1,Dp + top + 1,comp);
		int Z = top;top = 0;
		for(int i = 1;i <= Z;i ++)
			if(i == 1 || !same(Dp[i],Dp[i - 1]))
				Dp[ ++top] = Dp[i],pos[Dp[i].val] = top;
			else Dp[top].num ++,pos[Dp[i].val] = top;
		sort(queue + 1,queue + tot + 1,what);
		//cerr<<clock()<<endl;
		for(int i = 1;i <= top;i ++)
		{
		for(int j = 1;j <= tot;j ++)
			if(check(Dp[i].val,queue[j]))
				if(!exist[queue[j]]) Dp[i].k ++;
				else if(j == 1 || pos[queue[j]] != pos[queue[j - 1]]) inser(i,pos[queue[j]]);
		}memset(f,0,sizeof(f));
		for(int i = 1;i <= top;i ++)
			f[1][i][0] = Dp[i].num;
		f[1][top + 1][0] = O;
		cerr<<size<<endl;
		//cerr<<clock()<<endl;
		for(int i = 1;i <= n - 1;i ++)
		{
			for(int j = 1;j <= top;j ++) 
			{
				int Add = (1ll * f[i][j][0] * Dp[j].k) % Mod;
				f[i + 1][top + 1][1] += Add;
				if(f[i + 1][top + 1][1] > Mod) f[i + 1][top + 1][1] -= Mod;
				Add = (1ll * f[i][j][0] * (1ll * O - 1ll * Dp[j].k)) % Mod;
				f[i + 1][top + 1][0] += Add;
				if(f[i + 1][top + 1][0] > Mod) f[i + 1][top + 1][0] -= Mod;
				Add = (1ll * f[i][j][1] * O) % Mod;
				f[i + 1][top + 1][1] += Add;
				if(f[i + 1][top + 1][1] > Mod) f[i + 1][top + 1][1] -= Mod;
				for(int u = first[j];u;u = bian[u].next)
				{
					Add = (1ll * f[i][j][0] * Dp[bian[u].to].num) % Mod;
					f[i + 1][bian[u].to][1] += Add;
					if(f[i + 1][bian[u].to][1] > Mod) f[i + 1][bian[u].to][1] -= Mod;
				/*	Add = (1ll * f[i][j][1] * Dp[bian[u].to].num) % Mod;
					f[i + 1][bian[u].to][1] += Add;
					if(f[i + 1][bian[u].to][1] > Mod) f[i + 1][bian[u].to][1] -= Mod;*/
					f[i + 1][bian[u].to][0] -= Add;
					if(f[i + 1][bian[u].to][1] < 0) f[i + 1][bian[u].to][0] += Mod;
				}
				for(int k = 1;k <= top;k ++) 
				{
					Add = (1ll * f[i][j][0] * Dp[k].num) % Mod;
					f[i + 1][k][0] += Add;
					if(f[i + 1][k][0] > Mod) f[i + 1][k][0] -= Mod;
					Add = (1ll * f[i][j][1] * Dp[k].num) % Mod;
					f[i + 1][k][1] += Add;
					if(f[i + 1][k][1] > Mod) f[i + 1][k][1] -= Mod;
				}
			}
			for(int k = 1;k <= top;k ++) 
			{
				int Add;
				Add = (1ll * f[i][top + 1][0] * Dp[k].num) % Mod;
				f[i + 1][k][0] += Add;
				if(f[i + 1][k][0] > Mod) f[i + 1][k][0] -= Mod;
				Add = (1ll * f[i][top + 1][1] * Dp[k].num) % Mod;
				f[i + 1][k][1] += Add;
				if(f[i + 1][k][1] > Mod) f[i + 1][k][1] -= Mod;
			}
			int Add;
			Add = (1ll * f[i][top + 1][0] * O) % Mod;
			f[i + 1][top + 1][0] += Add;
			if(f[i + 1][top + 1][0] > Mod) f[i + 1][top + 1][0] -= Mod;
			Add = (1ll * f[i][top + 1][1] * O) % Mod;
			f[i + 1][top + 1][1] += Add;
			if(f[i + 1][top + 1][1] > Mod) f[i + 1][top + 1][1] -= Mod;
		}
		//cerr<<clock()<<endl;
		for(int i = 1;i <= top + 1;i ++)
			Ans = (Ans + f[n][i][1]) % Mod;
		printf("%d\n",Ans);
	}
	return 0;
}

然而又瞬间秒写了另外一个优化,就是对于第二层状态判一下相同的状态剪掉...(这还只是不完全剪枝,果然又快了一倍多....)

bool yiyang(const int &x,const int &y) {
	if(pos[x] == 0 || pos[y] == 0) return false;
	return pos[x] == pos[y];
}
sort(queue + 1,queue + tot + 1,what);
tot = unique(queue + 1,queue + tot + 1,yiyang) - queue - 1;


在处理完Dp之后其实就加了这么一句小小的话....

你可能感兴趣的:(bzoj4572)