题解 [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;
}
posted @ 2022-06-01 19:32  Administrator-09  阅读(3)  评论(0编辑  收藏  举报