COCI20102011 Contest#Final D (dp)
我们将一个操作序列看做由左右括号,空格构成的字符串,则序列大致长这个样子
( ext{_ ( ( ) _ ( ) ) ( _ ( ( ) ) ( })
很显然,一个失配的左括号只能在最外层出现,而空格可以出现在任意位置
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
",ans%P);
}