题解 [UNR #5] 提问系统
首先发现可以对这个栈建树,转化为树形 DP
那么一个暴力是令 \(f_{i, j, k, l}\) 为仅在 \(i\) 子树内,到 \(i\) 路径上出现过 R 的数量最大值为 \(j\),B 数量最大值为 \(k\),共出现 \(l\) 个 R 的方案数
转移可以填表+前缀和优化到 \(O(n^4)\)
然后正解:
你看这个 \(p_rp_b^2\),像不像某个经典套路?
于是转化为计算 考虑顺序地选择 1 个 R,1 个 B 和 1 个 B 的方案数
这样可以把最后一维优化到 \(O(8)\)
再考虑将状态改为记录 \(i\) 到根的路径上 R 的数量
这样的好处是到根的路径上 B 的数量就是 \(dep-cnt\)
那么就又砍掉了一维
这样复杂度就是 \(O(32n^2)\) 了,常数是真的大
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 2510
#define pb push_back
#define ll long long
//#define int long long
int n, cr, cb;
char op[N];
vector<int> to[N];
ll f[N][N][8], tem[8];
const ll mod=998244353;
int sta[N], dep[N], top, tot;
void dfs(int u) {
// cout<<"dfs: "<<u<<endl;
for (auto v:to[u]) dfs(v);
for (int i=0; i<=min(dep[u], cr); ++i) {
if (u&&i<cr) {
memset(tem, 0, sizeof(tem));
tem[0]=tem[1]=1;
for (auto v:to[u]) {
for (int j=7; ~j; --j) {
tem[j]=tem[j]*f[v][i+1][0]%mod;
for (int k=j; k; k=(k-1)&j) tem[j]=(tem[j]+tem[j^k]*f[v][i+1][k])%mod;
}
}
for (int j=0; j<8; ++j) f[u][i][j]=(f[u][i][j]+tem[j])%mod; //, cout<<"f: "<<u<<' '<<i<<' '<<j<<' '<<f[u][i][j]<<endl;
}
if (!u||dep[u]-i<=cb) {
memset(tem, 0, sizeof(tem));
if (u) tem[0]=tem[2]=tem[4]=tem[6]=1;
else tem[0]=1;
for (auto v:to[u]) {
for (int j=7; ~j; --j) {
tem[j]=tem[j]*f[v][i][0]%mod;
for (int k=j; k; k=(k-1)&j) tem[j]=(tem[j]+tem[j^k]*f[v][i][k])%mod;
}
}
for (int j=0; j<8; ++j) f[u][i][j]=(f[u][i][j]+tem[j])%mod; //, cout<<"f: "<<u<<' '<<i<<' '<<j<<' '<<f[u][i][j]<<endl;
}
}
}
signed main()
{
scanf("%d%d%d", &n, &cr, &cb);
for (int i=1; i<=2*n; ++i) {
scanf("%s", op+1);
if (op[2]=='u') {
sta[++top]=++tot;
dep[tot]=top;
to[sta[top-1]].pb(tot);
}
else --top;
}
dfs(0);
printf("%lld\n", (f[0][0][7]%mod+mod)%mod);
return 0;
}