BZOJ3162:独钓寒江雪 – 题解

主要思路

刚看这题我人都是懵的…仔细看了看能大概想到,定一个根然后做 DP 得到方案,然后对于相同形态的子树进行方案分配即可。(但是不会写)

具体而言,转换成有根树的方法就是用重心来做根(如果有两个重心的话,就加一个虚根),并且每次 DFS 完之后用 Hash 值排序,然后进行分组转移。

转移的时候,可以给每一种情况进行标号,然后用前缀的组合意义来做转移即可。具体:\( dp[i] = \sum_{uniqueType \in subtree} {dp[uniqueType] + |subtree| – 1 \choose |subtree|} \)。

如果加了虚点,那就判一下就好了。

// BZ3162.cpp
#include <bits/stdc++.h>

using namespace std;

const int MAX_N = 5e5 + 200, bitnum = 133, mod = 1e9 + 7;

int n, dep[MAX_N], up[MAX_N], siz[MAX_N], mx_val = 0x3f3f3f3f, groot, sub[MAX_N], dp[MAX_N][2], inv[MAX_N];
vector<int> G[MAX_N], roots;

void dfs(int u, int fa)
{
    dep[u] = dep[fa] + 1, up[u] = fa, siz[u] = 1;
    int max_component = 0;
    for (int v : G[u])
        if (v != fa)
            dfs(v, u), siz[u] += siz[v], max_component = max(max_component, siz[v]);
    max_component = max(max_component, n - siz[u]);
    if (max_component < mx_val)
        mx_val = max_component, roots.clear(), roots.push_back(u);
    else if (max_component == mx_val)
        roots.push_back(u);
}

int binomial(int n_, int k_)
{
    int ret = 1;
    for (int i = n_ - k_ + 1; i <= n_; i++)
        ret = 1LL * ret * i % mod;
    return 1LL * ret * inv[k_] % mod;
}

void hashDFS(int u, int fa)
{
    vector<int>::iterator delIt = G[u].end();
    for (vector<int>::iterator it = G[u].begin(); it != G[u].end(); it++)
        if (*it == fa)
        {
            delIt = it;
            break;
        }
    if (delIt != G[u].end())
        G[u].erase(delIt);
    for (int v : G[u])
        hashDFS(v, u);
    sort(G[u].begin(), G[u].end(), [](const int &rhs1, const int &rhs2) { return sub[rhs1] < sub[rhs2]; });
    sub[u] = 19260817, dp[u][0] = 1, dp[u][1] = 1;
    for (int i = 0, gx = 0, siz = G[u].size(); i < siz; i = gx)
    {
        for (gx = i; gx < siz && sub[G[u][gx]] == sub[G[u][i]]; gx++)
            sub[u] = ((1LL * sub[u] * bitnum % mod) ^ sub[G[u][gx]]);
        dp[u][0] = 1LL * dp[u][0] * binomial((0LL + dp[G[u][i]][0] + dp[G[u][i]][1] + gx - i - 1) % mod, gx - i) % mod;
        dp[u][1] = 1LL * dp[u][1] * binomial((0LL + dp[G[u][i]][0] + gx - i - 1) % mod, gx - i) % mod;
    }
    sub[u] = 1LL * sub[u] * bitnum % mod * bitnum % mod * bitnum % mod;
}

int main()
{
    // freopen("2.in", "r", stdin);
    scanf("%d", &n);
    int org_n = n;
    for (int i = 1, u, v; i <= n - 1; i++)
        scanf("%d%d", &u, &v), G[u].push_back(v), G[v].push_back(u);
    inv[0] = inv[1] = 1;
    for (int i = 2; i <= n; i++)
        inv[i] = 1LL * (mod - mod / i) * inv[mod % i] % mod;
    for (int i = 1; i <= n; i++)
        inv[i] = 1LL * inv[i] * inv[i - 1] % mod;
    dfs(1, 0);
    if (roots.size() >= 2)
    {
        int A = roots[0], B = roots[1];
        vector<int>::iterator itA, itB;
        for (vector<int>::iterator it = G[A].begin(); it != G[A].end(); it++)
            if (*it == B)
                itA = it;
        for (vector<int>::iterator it = G[B].begin(); it != G[B].end(); it++)
            if (*it == A)
                itB = it;
        G[A].erase(itA), G[B].erase(itB);
        G[A].push_back(groot = ++n), G[B].push_back(groot);
        G[groot].push_back(A), G[groot].push_back(B);
    }
    else
        groot = roots[0];
    hashDFS(groot, 0);
    if (groot == org_n + 1)
    {
        int ans = dp[groot][0];
        if (sub[roots[0]] == sub[roots[1]])
            ans = (0LL + ans + mod - binomial(dp[roots[0]][1] + 1, 2)) % mod;
        else
            ans = (0LL + ans + mod - 1LL * dp[roots[0]][1] * dp[roots[1]][1] % mod) % mod;
        printf("%d\n", ans);
    }
    else
        printf("%lld\n", (0LL + dp[groot][0] + dp[groot][1]) % mod);
    return 0;
}

Leave a Reply

Your email address will not be published. Required fields are marked *