COCI20102011 Contest#Final D (dp)

COCI20102011 Contest#Final D (dp)

我们将一个操作序列看做由左右括号,空格构成的字符串,则序列大致长这个样子

\(\text{_ ( ( ) _ ( ) ) ( _ ( ( ) ) ( }\)

很显然,一个失配的左括号只能在最外层出现,而空格可以出现在任意位置

dp一个括号序列让人想到区间dp,但是这个题目的区间实际只需要用长度就可以描述

\(dp[t][l][r][f1][f2]\)表示用\(t\)的时间从\(l\)走到\(r\)\(f1\)表示是不是最外层括号,\(f2\)表示当前\(dp\)是否受到单纯括号序列的限制

其中,引入的单纯括号序列是为了防止出现重复转移,其意思就是这个括号序列两端必须是一对匹配的左右括号,而中间随意

转移大致如下:

1.那么对于非单纯的括号序列,可以在序列插入空格或者失配的左括号(需要满足\(f1\)),从\(dp[t-1]\)转移过来

2.对于任何的括号序列,都可以在两端找到匹配的的左右括号,从\(dp[t-2]\)转移过来,且完成匹配后\(f1\)应为\(0\)

3.且一个非单纯的括号序列是可以分割的,为了不重复,强制分割的左序列是单纯的即可

#include<bits/stdc++.h>
using namespace std;
#pragma GCC optimize(2)
typedef long long ll;
#define reg register
#define rep(i,a,b) for(reg int i=a;i<=b;++i)
#define drep(i,a,b) for(reg int i=a;i>=b;--i)
char IO;
int rd(){
	int s=0;
	while(!isdigit(IO=getchar()));
	do s=(s<<1)+(s<<3)+(IO^'0');
	while(isdigit(IO=getchar()));
	return s;
}

const int N=51,P=10007;

int n,m,T;
int E[N][N];
int dp[N][N][N][2][2];

int main(){
	n=rd(),m=rd(),T=rd();
	memset(E,-63,sizeof E);
	rep(i,1,m) {
		int u=rd(),v=rd(),c=IO==' '?getchar():IO;
		if(c>='A' && c<='Z') E[u][v]=c-'A'+1;
		else if(c>='a' && c<='z') E[u][v]='a'-c-1;
		else E[u][v]=0;
	}
	rep(i,1,n) rep(j,0,1) dp[0][i][i][j][0]=1;
	rep(k,1,T){
		rep(l,1,n) rep(r,(k<T?1:n),n) rep(fl,0,1) rep(fl2,0,1) {
			ll res=0;
			if(!fl2) {
				rep(i,2,k-1) rep(j,1,n) res+=dp[i][l][j][0][1]*dp[k-i][j][r][fl][0];
				rep(i,1,n) if(E[l][i]>=0 && (!E[l][i] || fl)) res+=dp[k-1][i][r][fl][fl2];
			}
			if(k>1) rep(i,1,n) if(E[l][i]>0) rep(j,1,n) if(E[j][r]+E[l][i]==0) res+=dp[k-2][i][j][0][0];
			dp[k][l][r][fl][fl2]=res%P;
		}
	}
	int ans=0;
	rep(i,1,T) ans+=dp[i][1][n][1][0];
	printf("%d\n",ans%P);
}



posted @ 2020-08-27 21:23  chasedeath  阅读(169)  评论(0编辑  收藏  举报