树上dp问题

给定一棵n个节点的有根树,节点编号范围为1∼n,其中根节点编号为1。现在你需要给树上每个节点赋值左括号(或者右括号),问你有多少种赋值方案,使得赋值完毕后从根节点到达每一个叶子节点的唯一简单路径所经过的点构成的括号串是一个合法括号串(求出方案数对998244353取模的结果)?

我们递归的定义合法括号串:

  • 空括号串是合法的。
  • A合法,则(A)合法。
  • AB合法,则AB合法。
格式

输入格式:

第一行一个整数T(1≤T≤500),表示测试数据组数,对于每组测试数据:

第一行一个整数n(1≤n≤3000)。

接下来n−1n−1行,每行两个整数u,v(1≤u,v≤n),表示点uu与点vv之间有一条边。

保证单个测试点的所有组测试数据的nn之和不超过3000。

输出格式:

对于每组测试数据:

输出一行一个整数,表示答案。

样例 1

输入:

复制代码
1
4
1 2
2 3
3 4

复制

输出:

复制代码
2

复制

样例 2

输入:

复制代码
1
11
1 2
1 3
1 4
2 5
2 8
3 10
5 6
5 7
8 9
10 11

复制

输出:

复制代码
4

复制

备注

对于样例11,这是一条链,合法的括号串有()()(())两种,答案为2。

#include <iostream>

#include <vector>

using namespace std;

const int MOD = 998244353;

vector<int> adj3005;

long long dp30053005;//来到u节点前,欠j个左括号,到根后能形成的合法方案数

int n;

void dfs(int u, int p) {

// 1. 先递归处理子节点

vector<int> children;

for (int v : adju) {

if (v != p) {

children.push_back(v);

dfs(v, u);

}

}

// 2. 计算当前节点在不同初始余额 j 下的方案数

// j 是"进入节点 u 之前"的余额

for (int j = 0; j <= n; ++j) {

long long ways_as_L = 0;

long long ways_as_R = 0;

// 情况 A: 节点 u 填左括号 '('

// 填完后余额变为 j + 1

if (j + 1 <= n) {

if (children.empty()) {

// 如果 u 是叶子,填 '(' 后余额为 j+1

// 路径结束要求余额必须为 0,所以 j+1 == 0 (不可能)

ways_as_L = 0;

} else {

ways_as_L = 1;

for (int v : children) {

ways_as_L = (ways_as_L * dpvj + 1) % MOD;

}

}

}

// 情况 B: 节点 u 填右括号 ')'

// 填完后余额变为 j - 1

if (j - 1 >= 0) {

if (children.empty()) {

// 如果 u 是叶子,填 ')' 后余额为 j-1

// 路径结束要求余额必须为 0,即 j 必须是 1

ways_as_R = (j - 1 == 0 ? 1 : 0);

} else {

ways_as_R = 1;

for (int v : children) {

ways_as_R = (ways_as_R * dpvj - 1) % MOD;

}

}

}

dpuj = (ways_as_L + ways_as_R) % MOD;

}

}

void solve() {

cin >> n;

for (int i = 1; i <= n; i++) {

adji.clear();

for (int j = 0; j <= n; j++) dpij = 0;

}

for (int i = 0; i < n - 1; i++) {

int u, v;

cin >> u >> v;

adju.push_back(v);

adjv.push_back(u);

}

// 题目要求根节点到叶子的路径。根节点编号为 1。

dfs(1, 0);

// 最终答案是:进入根节点 1 之前,余额为 0 的方案数

cout << dp10 << endl;

}

int main() {

ios::sync_with_stdio(false);

cin.tie(0);

int T;

cin >> T;

while (T--) {

solve();

}

return 0;

}

相关推荐
学计算机的计算基2 分钟前
回溯算法下篇:四道经典题讲透约束剪枝、原地标记、预计算与状态压缩
java·笔记·算法
霸道流氓气质29 分钟前
SpringBoot中实现告警判定算法-位报警与模拟量阈值-技术详解
spring boot·算法·jquery
行者全栈架构师31 分钟前
混元 Hy3 Agent 实战:季度报告 3 小时变 40 分钟
算法·架构·代码规范
兰令水1 小时前
hot100【acm版】【2026.7.21打卡-java版本】
java·开发语言·算法·leetcode·面试
好好沉淀1 小时前
主键选择(自增 vs UUID vs 雪花算法)
java·算法
星恒随风2 小时前
C++ STL 详解:list 的使用、迭代器失效、模拟实现与 vector 对比
开发语言·数据结构·c++·笔记·学习·list
退休倒计时2 小时前
【每日一题】LeetCode 131. 分割回文串 TypeScript
算法·leetcode·typescript
小白说大模型2 小时前
AI Agent 调试实战:链路追踪、Prompt 可视化与异常定位的系统方法
java·人工智能·python·算法·prompt
梅梅绵绵冰2 小时前
算法题-矩阵
数据结构·算法·矩阵
atunet2 小时前
关于从哈希冲突看缓存设计的实践与权衡7
算法