P14721 题解
albertting · · 题解
我怎么做了一个世纪。
歪解
:::info[点击观看抽搐思考过程] 首先菜菜的作者想到了只要让没有水豚的所有点互不相邻就是正确的!然后就是一个没有上司的舞会!
然后发现样例二可以摆在
开始思考一个水豚的贡献是什么,发现可以让一只水豚管辖两个点,然后一直穿过中间的边,这样就可以覆盖完整。然后变成一个边覆盖!只要让所有不被覆盖的边全部都不相邻就是正确的。但是发现有重合顶点的不是很好做。直接放在点上不就能做吗。
然后发现一只水豚其实可以刻画成管辖一个或者两个点,于是这个东西就好维护了。最开始设计成一个点的子树里面有多少个颜色以及当前节点要不要和父亲匹配。然后样例二又爆炸了!为什么呢,因为不同的分割方式最后得到的摆放集合是一样的,比如
| 怎么办呢,我们可以考虑把当前节点的放置状态也记录进去,于是就能做了。后面是正解。 |
|---|
正解
用
| 含义 | |
|---|---|
对的你说为啥独立放在
合并儿子的时候记录以下内容:
其中:
转移(
根节点不能有父亲,所以
复杂度的分析大概是每个无序点对只在 LCA 处被合并一次,
Code
// Author: albertting
// Time: 2026/11/12 12:18:00
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using u32 = uint32_t;
using i64 = int64_t;
using u64 = uint64_t;
constexpr i32 MOD = 998244353;
void addto(i32 &x, i32 y) {
x += y;
if(x >= MOD) x -= MOD;
}
vector<i32> conv(const vector<i32> &a, const vector<i32> &b) {
vector<i32> res(a.size() + b.size() - 1, 0);
for(size_t i = 0; i < a.size(); i++) {
if(a[i] == 0) continue;
for(size_t j = 0; j < b.size(); j++) {
res[i + j] = (res[i + j] + (i64)a[i] * b[j]) % MOD;
}
}
return res;
}
vector<i32> solve(i32 N, vector<i32> U, vector<i32> V) {
vector<vector<i32> > adj(N);
for(i32 i = 0; i < N - 1; i++) adj[U[i]].push_back(V[i]), adj[V[i]].push_back(U[i]);
vector<i32> par(N, -1), ord;
ord.reserve(N);
vector<i32> st = {0};
par[0] = 0;
while(!st.empty()) {
i32 u = st.back();
st.pop_back();
ord.push_back(u);
for(i32 w : adj[u]) {
if(w == par[u]) continue;
par[w] = u;
st.push_back(w);
}
}
vector<vector<array<i32, 4> > > dp(N);
for(auto it = ord.rbegin(); it != ord.rend(); it++) {
i32 u = *it;
vector<i32> Z(1, 1), Y(1, 1), E(1, 0);
for(i32 v : adj[u]) {
if(v == par[u]) continue;
auto &g = dp[v];
i32 m = (i32)g.size();
vector<i32> p(m), q(m), r(m);
for(i32 k = 0; k < m; k++) {
i32 a0 = g[k][0], a1 = g[k][1], b = g[k][2], c = g[k][3];
p[k] = ((a0 + a1) % MOD + b) % MOD, q[k] = (a1 + b) % MOD, r[k] = c;
}
vector<i32> Zn = conv(Z, p), En = conv(E, p), Zr = conv(Z, r);
size_t L = max(En.size(), Zr.size());
En.resize(L, 0), Zr.resize(L, 0);
for(size_t k = 0; k < L; k++) addto(En[k], Zr[k]);
Z = move(Zn), E = move(En), Y = conv(Y, q);
vector<array<i32, 4> >().swap(dp[v]);
}
i32 m = (i32)Z.size();
vector<array<i32, 4>> h(m + 1);
for(i32 k = 0; k <= m; k++) {
i32 a0 = k >= 1 ? Z[k - 1] : 0, a1 = k >= 1 ? E[k - 1] : 0, b = 0, c = 0;
if(k < m) {
b = Z[k] - Y[k];
if(b < 0) b += MOD;
c = Y[k];
}
h[k] = {a0, a1, b, c};
}
dp[u] = move(h);
}
vector<i32> ans(N + 1, 0);
auto &g = dp[0];
for(i32 k = 0; k <= N; k++) if(k < (i32)g.size()) ans[k] = ((g[k][0] + g[k][1]) % MOD + g[k][2]) % MOD;
return ans;
}
// int main() {
// ios::sync_with_stdio(false);
// cin.tie(0), cout.tie(0), cerr.tie(0);
// i32 N; cin >> N;
// vector<i32> U(max(N - 1, 0)), V(max(N - 1, 0));
// for(i32 i = 0; i < N - 1; i++) cin >> U[i] >> V[i];
// vector<i32> ans = solve(N, U, V);
// for(i32 k = 0; k <= N; k++) cout << ans[k] << (k == N ? '\n' : ' ');
// return 0;
// }