Solution
树上 dp,设 为以 为根的子树中好的非空子集数量。若 为叶节点, 则 。下面假设 非叶节点, 为 的子节点:
- 点集取 ,则 的子树可任意取,根据乘法原理,贡献为 。
- 点集不取 ,则只能取 的一棵子树,根据加法原理,贡献为 。
由于 保证 子树非空,因此情况一中贡献多出来的 ,为整棵 子树不取的情况。由此
根据这个式子,若 是 的其中一个子节点且 减少 ,则 减少
并设其为 。现在假设 减少 ,则 的减少值为 。又设 为 这条链上所有 的前缀积,则删去子树 的答案为
所以先用第一遍 dfs 求出 、,再用第二遍 dfs 求出 。
注意求 的时候,不能直接除,需要用 exgcd 求乘法逆元。
时间复杂度
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 5e5 + 10, mod = 998244353;
ll n, q, d[N], f[N], fa[N];
vector<ll> g[N];
void exgcd(ll a, ll b, ll &x, ll &y)
{
if (b == 0) {
x = 1; y = 0;
return;
}
exgcd(b, a % b, y, x);
y = y - a / b * x;
}
void dfs1(ll u, ll pre)
{
ll mul = 1, sum = 0;
fa[u] = pre;
for (ll v : g[u]) {
if (v == pre)
continue;
dfs1(v, u);
sum = (sum + f[v] + mod) % mod;
mul = (mul * (f[v] + 1) % mod + mod) % mod;
}
f[u] = (sum + mul + mod) % mod;
for (ll v : g[u]) {
if (v == pre)
continue;
ll inv, y;
exgcd(f[v] + 1, mod, inv, y);
d[v] = (mul * inv % mod + 1 + mod) % mod;
}
}
void dfs2(ll u)
{
d[u] = (d[u] * d[fa[u]] % mod + mod) % mod;
for (ll v : g[u]) {
if (v == fa[u])
continue;
dfs2(v);
}
}
int main()
{
scanf("%lld", &n);
for (int i = 1; i < n; i++) {
ll u, v;
scanf("%lld%lld", &u, &v);
g[u].push_back(v);
g[v].push_back(u);
}
dfs1(1, 0);
d[0] = d[1] = 1;
dfs2(1);
scanf("%lld", &q);
while (q--) {
ll x;
scanf("%lld", &x);
printf("%lld\n", (f[1] - f[x] * d[x] % mod + mod) % mod);
}
return 0;
}