洛谷 P14235 [COI 2011] 卡车 / KAMION 题解

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。加上题目的距离限制,设出如下状态。

  1. \(f_{i,j,k}\)\(i\to j\),路径长度为 \(k\) 且操作序列形如 \(\texttt{(S)}\) 的路径数。
  2. \(g_{i,j,k}\)\(i\to j\),路径长度为 \(k\) 且操作序列形如 \(\texttt{\_(S)\_(T)\_\_\dots}\)(若干个 \(\texttt{(S)}\) 插入空格)的路径数。
  3. \(h_{i,k}\)\(i\to n\),路径长度为 \(k\) 的合法路径数。

最终答案即为 \(\sum_{i=1}^kh_{1,k}\)

然后考虑状态转移。

  1. \(f\):由一对括号夹 \(g\) 转移。
  2. \(g\):由空格或 \(f\) 拼上另一个 \(g\) 转移。
  3. \(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]){  // 一对括号夹g
                    if(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]){  // 空格+另一个g
                    if(!w1) (g[i][j][k]+=g[v1][j][k-1])%=P;
                }
                rept(u,1,n){  // f+另一个g
                    rept(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]){  // 空格/左括号+另一个h
                if(!w||isupper(w)){
                    (h[i][k]+=h[v][k-1])%=P;
                }
            }
            rept(u,1,n){  // f+另一个h
                rept(t,2,k){
                    (h[i][k]+=f[i][u][t]*h[u][k-t])%=P;
                }
            }
        }
        (ans+=h[1][k])%=P;
    }
    cout<<ans;
    return 0;
}
posted @ 2026-01-24 14:14  xiaoniu142857  阅读(0)  评论(0)    收藏  举报