题解:P17145 [NOI 2026] 彩虹树
WorldMachine · · 题解
很深刻的题。
题目所求等价于:有多少对
碰到这种长相奇特的计数题,可以先想一下怎么判定。
首先显然要有
设
考虑
先合并子树,对于子树
同时对于整棵子树,所有儿子内部颜色有
内部外部两类颜色是独立的,因此下界可以分别取到。因此
再考虑加入
于是现在可以做到
下一步肯定是要 DP of DP 了。
设
然而由于
然后这样状态就是四维的,直接做是
想到容斥,分不限制
第一种是好做的:只需维护计算子树下界所需的两个量
对于第二种,限定
也就是说这时只需要考虑
对于第三种,限定
也就是说这时只需要考虑
至此状态变成二维的,这部分转移就变成了
加入
代码还是很好写的。
#include <bits/stdc++.h>
// #include "rainbow.h"
using namespace std;
#define il inline
#define For(i, l, r) for (int i = (l), _r_ = (r); i <= _r_; i++)
typedef long long ll;
const int N = 205, P = 998244353;
il void _a(int &x, int y) { (x += y) >= P && (x -= P); }
il void _d(int &x, int y) { (x -= y) < 0 && (x += P); }
int n, sz[N], f[N][N][N], g[N][N], tp[N][N];
basic_string<int> G[N];
/*
首先有 max c[v] <= c[u] <= (sum c[v]) + 1
设 u 子树内 需要到 u 上面找匹配的 颜色数 最小为 dp[u]
有解 <=> dp[1] = 0
可以得到 dp 的转移,塞到 dp of dp 里面
要记录 sum dp[v],sum c[v],max dp[v],max c[v]
问题卡在第一行说的限制,但可以定向容斥掉
o = 1:c[x] 无限制
状态:g[max dp[v]][sum (c[v] - dp[v])]
o = 0:c[x] < max c[v]
状态:g[max dp[v]][max c[v]]
o = -1:c[x] > (sum c[v]) + 1
状态:g[sum c[v]][sum (c[v] - dp[v])]
这样限制变成二维的,复杂度 O(N^4)
*/
void DP(int u, int o) {
For(i, 0, n) For(j, 0, n) g[i][j] = 0; sz[u] = 0, g[0][0] = 1;
for (int v : G[u]) {
For(i, 0, sz[u]) For(j, 0, sz[u]) tp[i][j] = g[i][j], g[i][j] = 0;
For(i, 0, sz[u]) For(j, 0, sz[u]) if (tp[i][j]) For(x, 0, sz[v]) For(y, x, sz[v])
_a(g[~o ? max(i, x) : i + y][!o ? max(j, y) : j + y - x], (ll)tp[i][j] * f[v][x][y] % P);
sz[u] += sz[v];
}
For(i, 0, sz[u]) For(j, 0, sz[u]) if (g[i][j]) {
if (o > 0) For(k, 0, sz[u] + 1) _a(f[u][max(i, k - j)][k], g[i][j]);
else if (!o) For(k, 0, j - 1) _d(f[u][i][k], g[i][j]);
else For(k, max(i + 1 + 1, j), sz[u] + 1) _d(f[u][k - j][k], g[i][j]);
}
}
void dfs(int u) {
if (G[u].empty()) return void(f[u][0][1] = f[u][1][1] = sz[u] = 1);
for (int v : G[u]) dfs(v); for (int o : {1, 0, -1}) DP(u, o); sz[u]++;
For(i, 0, sz[u]) For(j, 0, sz[u]) tp[i][j] = f[u][i][j], f[u][i][j] = 0;
/* i == 0 */ For(j, 0, sz[u]) _a(f[u][0][j], tp[0][j]), _a(f[u][1][j], tp[0][j]);
For(i, 1, sz[u]) For(j, 0, sz[u]) _a(f[u][i - 1][j], tp[i][j]), _a(f[u][i][j], tp[i][j]);
}
int rainbow(int c, int n, vector<int> f) {
::n = n; For(i, 1, n - 1) G[f[i] + 1] += i + 1;
return dfs(1), accumulate(::f[1][0], ::f[1][0] + 1 + n, 0ll) % P;
}
#ifdef WorldMachine
int main() {
ios::sync_with_stdio(0), cin.tie(0);
int c, n; cin >> c >> n; vector<int> f(n);
for (int i = 1; i < n; i++) cin >> f[i];
cout << rainbow(c, n, f);
}
#endif