COCI20102011 Contest#Final D (dp)
阿新 • • 發佈:2020-08-27
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); }