题解 [AT_dp_v]

发布于 2025-11-09 10:02 · 作者 jnzg

AT_dp_v Subtree

题目大意

给出结点数为 $n$ 的树,求以每个结点为根,对包含根的联通块计数。

思路

显然是换根 dp。

首先考虑一个根节点的情况,设 $f_u$ 表示以 $u$ 为根节点的子树,强制选 $u$ 的方案数,于是有:

$$ f_u=\prod\limits_{v\in son_u}(f_v+1) $$

考虑换根,设 $ans_u$ 表示以 $u$ 为整棵树的根,强制选 $u$ 的方案数。于是有:

$$ ans_u=\frac{ans_{fa_u}}{f_u+1}\times f_u $$

然而要对 $m$ 取模,但 $m$ 不一定是质数,所以没法做除法。

考虑暴力相乘,设 $g_u$ 为除去 $u$ 的子树,以 $u$ 为根节点,强制选 $u$ 的方案数,得到:

$$ g_u=g_{fa_u}\prod_{v\in fa_u \operatorname{and}v\ne u}(f_v+1) $$

于是问题变成如何求 $\prod_{v\in fa_u \operatorname{and}v\ne u}(f_v+1)$。

想不到前缀后缀积怎么办?

不难想到分治,设 $fa_u$ 的儿子区间 $[l,r]$ 表示第 $l$ 到第 $r$ 个儿子,设区间中点 $mid$。

先将区间 $[mid+1,r]$ 的 $f_v+1$ 乘起来,记到 $mul$ 中,然后递归区间 $[l,mid]$,再把 $mul$ 还原,将区间 $[l,mid]$ 的 $f_v+1$ 乘起来,递归区间 $[mid+1,r]$。

当 $l=r$ 时 $mul$ 即为 $\prod_{v\in fa_u \operatorname{and}v\ne u}(f_v+1)$,于是就得到了 $g_u$。

最终答案即为 $f_u\times g_u$。

空间虽然小了点,但时间复杂度为 $O(n\log n)$。

Code:

#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define lp(i, j, n) for(int i = j; i <= n; ++i)
#define dlp(i, n, j) for(int i = n; i >= j; --i)
#define mst(n, v) memset(n, v, sizeof(n))
#define mcy(n, v) memcpy(n, v, sizeof(v))
#define INF 1e18
#define MAX4 0x3f3f3f3f
#define MAX8 0x3f3f3f3f3f3f3f3f
#define pii pair<int, int>
#define pll pair<ll, ll>
#define co(x) cerr << (x) << ' '
#define cod(x) cerr << (x) << endl
#define fi first
#define se second
#define eps 1e-8
#define lc(x) ((x) << 1)
#define rc(x) ((x) << 1 ^ 1)
#define pb(x) emplace_back(x)

using namespace std;

const int N = 100010;

int n, m;
int f[N], g[N];

struct edge { int v, nxt; } E[N << 1];
int en, hd[N];

void add(int u, int v) { E[++en] = { v, hd[u] }, hd[u] = en; }

void dfs1(int u, int fa) {
    f[u] = 1;
    for(int i = hd[u]; i; i = E[i].nxt) {
        int v = E[i].v;
        if(v == fa) continue;
        dfs1(v, u), f[u] = 1ll * f[u] * (f[v] + 1) % m;
    }
}

int son[N], tot;
ll mul;
void solve(int l, int r) {
    if(l == r) return g[son[l]] = (mul + 1) % m, void();
    ll t = mul; int mid = l + r >> 1;
    lp(i, mid + 1, r) mul = mul * (f[son[i]] + 1) % m;
    solve(l, mid);
    mul = t; lp(i, l, mid) mul = mul * (f[son[i]] + 1) % m;
    solve(mid + 1, r);
}

void dfs2(int u, int fa) {
    tot = 0, mul = g[u];
    for(int i = hd[u]; i; i = E[i].nxt) {
        if(E[i].v != fa) son[++tot] = E[i].v;
    }
    if(tot >= 1) solve(1, tot);
    for(int i = hd[u]; i; i = E[i].nxt) {
        if(E[i].v != fa) dfs2(E[i].v, u);
    }
}

signed main() {
    // freopen(".in", "r", stdin);
    // freopen(".out", "w", stdout);
#ifndef READ
    ios::sync_with_stdio(false);
    cin.tie(0);
#endif
    cin >> n >> m;
    int u, v;
    lp(i, 1, n - 1) cin >> u >> v, add(u, v), add(v, u);
    dfs1(1, 0), g[1] = 1, dfs2(1, 0);
    lp(i, 1, n) cout << 1ll * f[i] * g[i] % m << endl;
    return 0;
}

评论

登录 后发表评论。

暂无评论,快来抢沙发!