Loading

[HDU 6810] Imperative Meeting

前言

这个题居然有一点思路, 可喜可贺

太困难啦

思路

转化题意, 令

\[f(S) = \min_{u \in \mathbb{V}} \left(\sum_{v \in \mathbb{S}} \text{dist}(u, v)\right) \]

\(\displaystyle \sum_{\lvert S\rvert = m} f(S)\)

那么怎么做?

之前有一道题的 \(\rm{trick}\) , 我们先计算每条边的期望, 然后统计答案即可

这道题怎么办?

(以下的 "点" 都是指 \(\mathbb{S}\) 中的点)

首先我们需要观察到一个性质, 对于一个点, 我们以它作为根节点, 那么其一颗子树内的点一定会经过一条连向其父亲的边去到另一个子树, 其他子树的点也会经过父亲到子树的点去到子树中的点

类似于带权重心的想法, 最优的情况下, 其中一颗子树中点的个数一定不超过 \(\lfloor \frac{m - 1}{2} \rfloor\) , 这样才会最优

那么对于一条边, 我们只需要知道 \(\mathbb{S}\) 中的点在其两侧的分布情况即可

假设一条边连接的一个部分大小为 \(s\) , 那么这条边的总贡献可以求出

\[f = \sum_{i = 1}^{m - 1} {s \choose i} {n - s \choose m - i} \cdot \min{(m - i, i)} \]

这是 \(\mathcal{O} (n ^ 2)\) 的, 那么就止步于此吧

实现

框架

首先计算每条边两侧的大小:
\(\rm{dfs}\) 即可

然后按照公式计算即可

代码

这里放 \(\rm{QCZ}\) 大佬的代码

#include <bits/stdc++.h>

#define int long long
using namespace std;

void read(int &x)
{
    x = 0;
    char ch = getchar();
    while (!isdigit(ch))
        ch = getchar();
    while (isdigit(ch))
        x = x * 10 + (ch ^ 48), ch = getchar();
}

const int maxn = 1000010, mod = 1000000007;
int n, m, fac[maxn], inv[maxn], sz[maxn], ans;
vector<int> vec[maxn];

int quickpow(int a, int b)
{
    int res = 1;
    while (b)
    {
        if (b & 1)
            res = res * a % mod;
        a = a * a % mod, b >>= 1;
    }
    return res;
}
int calc(int a, int b)
{
    if (a < b)
        return 0;
    return fac[a] * inv[b] % mod * inv[a - b] % mod;
}

void dfs(int u, int fa)
{
    sz[u] = 1;
    for (auto v : vec[u])
        if (v != fa)
            dfs(v, u), sz[u] += sz[v];
    int st = max(1ll, m - n + sz[u]), ed = min(m / 2, sz[u]);
    for (int i = st; i <= ed; i++)
        ans = (ans + calc(sz[u], i) * calc(n - sz[u], m - i) % mod * i % mod) % mod;
    st = max(m / 2 + 1, m - n + sz[u]), ed = min(m - 1, sz[u]);
    for (int i = st; i <= ed; i++)
        ans = (ans + calc(sz[u], i) * calc(n - sz[u], m - i) % mod * (m - i) % mod) % mod;
}

signed main()
{
    //	freopen ("data.txt", "r", stdin);
    fac[0] = 1;
    for (int i = 1; i < maxn; i++)
        fac[i] = fac[i - 1] * i % mod;
    inv[maxn - 1] = quickpow(fac[maxn - 1], mod - 2);
    for (int i = maxn - 2; i >= 0; i--)
        inv[i] = inv[i + 1] * (i + 1) % mod;
    read(n), read(m);
    for (int i = 2, u; i <= n; i++)
    {
        read(u);
        vec[u].push_back(i);
        vec[i].push_back(u);
    }
    dfs(1, 0);
    printf("%lld", ans);
    return 0;
}

总结

善于利用组合意义来解决问题

posted @ 2024-12-17 15:29  Yorg  阅读(17)  评论(0)    收藏  举报