📅  最后修改于: 2023-12-03 15:10:38.312000             🧑  作者: Mango
有序树上的组合问题是指在一棵有根有序树中,从每个节点出发,可以选择经过该节点或不经过该节点,形成的所有路径的集合。这种问题在算法竞赛中经常出现,其典型应用包括计数、最大权路径和最小路径覆盖等问题。
在有序树上的组合问题中,我们可以通过DP解决该问题。设$f(u,0/1)$表示从节点$u$开始,经过/不经过该节点所组成路径的个数,则有以下状态转移方程:
$$f(u,0)=\sum_{v\in son_u}f(v,1)$$$$f(u,1)=\prod_{v\in son_u}(f(v,0)+f(v,1))$$
其中$son_u$表示节点$u$的所有儿子节点。通过这两个方程,我们可以计算出从每个节点出发的所有路径组合,最终总路径组合数即为$f(root,0)+f(root,1)$。
#include <iostream>
#include <vector>
using namespace std;
const int MAXN = 1e5 + 10;
long long f[MAXN][2]; // DP数组,使用long long类型避免整数溢出
vector<int> son[MAXN];
void dfs(int u) {
for (int i = 0; i < son[u].size(); i++) {
int v = son[u][i];
dfs(v);
f[u][0] += f[v][1]; // 转移方程1
}
f[u][1] = 1;
for (int i = 0; i < son[u].size(); i++) {
int v = son[u][i];
f[u][1] *= (f[v][0] + f[v][1]); // 转移方程2
}
}
int main() {
int n;
cin >> n;
for (int i = 1; i < n; i++) {
int u, v;
cin >> u >> v;
son[u].push_back(v); // 构造有序树
}
dfs(1);
cout << f[1][0] + f[1][1] << endl;
return 0;
}
n = int(input())
son = [[] for _ in range(n+1)]
f = [[0]*2 for _ in range(n+1)]
def dfs(u):
for v in son[u]:
dfs(v)
f[u][0] += f[v][1] # 转移方程1
f[u][1] = 1
for v in son[u]:
f[u][1] *= (f[v][0] + f[v][1]) # 转移方程2
for i in range(1, n):
u, v = [int(x) for x in input().split()]
son[u].append(v) # 构造有序树
dfs(1)
print(f[1][0] + f[1][1])
在有序树上的组合问题中,我们需要遍历每个节点,对于每个节点,需要遍历其所有子节点,因此时间复杂度为$O(n)$。