Solution
考虑将一个操作序列看成带空格括号串,其中类型为 \(1,2,3\) 的边分别对应左括号、右括号和空格。
先约定大写字母 \(\texttt{S,T,\dots}\) 指代不同的合法带空格括号串。
不难发现,最后的序列大致长成 \(\texttt{\color{red}\_(\color{green}(\_(())\_(\_))\color{red}\_(((\color{green}()\color{red}(\color{green}()\color{red}\_\_(\color{none}\dots}\),可以看成若干个形如 \(\texttt{\color{green}(S)}\) 的串插入 \(\texttt{\color{red}(}\) 和 \(\texttt{\color{red}\_}\) 形成的。
于是想到区间 DP。加上题目的距离限制,设出如下状态。
- \(f_{i,j,k}\):\(i\to j\),路径长度为 \(k\) 且操作序列形如 \(\texttt{(S)}\) 的路径数。
- \(g_{i,j,k}\):\(i\to j\),路径长度为 \(k\) 且操作序列形如 \(\texttt{\_(S)\_(T)\_\_\dots}\)(若干个 \(\texttt{(S)}\) 插入空格)的路径数。
- \(h_{i,k}\):\(i\to n\),路径长度为 \(k\) 的合法路径数。
最终答案即为 \(\sum_{i=1}^kh_{1,k}\)。
然后考虑状态转移。
- \(f\):由一对括号夹 \(g\) 转移。
- \(g\):由空格或 \(f\) 拼上另一个 \(g\) 转移。
- \(h\):由空格、左括号或 \(f\) 拼上另一个 \(h\) 转移。
不难发现转移过程中序列长度 \(k\) 严格递增。因此最外层循环需要从小到大枚举 \(k\)。
若将 \(n,k\) 视为同阶则时间复杂度为 \(O(n^5)\),可以通过。
Code
#include <bits/stdc++.h>
#define rept(i,a,b) for(int i(a);i<=b;++i)
#define eb emplace_back
using namespace std;
constexpr int N=51,M=51,P=10007;
struct Edge{Edge(int _v=0,char _w=0):v(_v),w(_w){}int v;char w;
};
int n,m,len,ans;
int f[N][N][N],g[N][N][N],h[N][N];
vector<Edge> G[N],H[N]; // G为原图,H为反图
string s;
signed main(){cin.tie(0)->sync_with_stdio(0);cin>>n>>m>>len;getline(cin,s);rept(i,1,n) g[i][i][0]=1;while(m--){int u,v;char w;getline(cin,s);istringstream line(s);line>>u>>v;if(!(line>>w)) w=0,++g[u][v][1];G[u].eb(v,w);H[v].eb(u,w);}rept(k,2,len){rept(i,1,n){rept(j,1,n){// 计算f[i][j][k]for(auto [v1,w1]:G[i]){ // 一对括号夹gif(isupper(w1)){for(auto [v2,w2]:H[j]){if(w2-w1=='a'-'A'){(f[i][j][k]+=g[v1][v2][k-2])%=P;}}}}// 计算g[i][j][k]for(auto [v1,w1]:G[i]){ // 空格+另一个gif(!w1) (g[i][j][k]+=g[v1][j][k-1])%=P;}rept(u,1,n){ // f+另一个grept(t,2,k){(g[i][j][k]+=f[i][u][t]*g[u][j][k-t])%=P;}}}}}h[n][0]=1;rept(k,1,len){rept(i,1,n){// 计算h[i][k]for(auto [v,w]:G[i]){ // 空格/左括号+另一个hif(!w||isupper(w)){(h[i][k]+=h[v][k-1])%=P;}}rept(u,1,n){ // f+另一个hrept(t,2,k){(h[i][k]+=f[i][u][t]*h[u][k-t])%=P;}}}(ans+=h[1][k])%=P;}cout<<ans;return 0;
}