题目链接:Codeforces1109D

题意

求满足以下条件的带标号树的个数:

  • 边权在\([1,m]\cap Z\)

  • \(a\)到点\(b\)间的边权和为\(m\)

数据范围:\(n,m≤1e6\)

题解

枚举\(a\),\(b\)间点数为\(i\),可以知道方案数为:

\[ \dbinom{n - 2}{i}\dbinom{m-1}{i}i!m^{n-2-i} f(i) \]

\(\dbinom{n-2}{i}i!\) 是在剩下\(n-2\)个点中选\(i\)个排在\(a\),\(b\)之间。

\(\dbinom{m-1}{i}\)\(i+1\)条边和为\(m\)的方案数。

\(m^{n-2-i}\) 是不在\(a\),\(b\)之间的点可以自由选择权值。

\(f(i)\) 是有一个长为\(i+2\)的链(以\(a\),\(b\)为端点)和\(n-i-2\)个独立的点的生成树个数。

事实上,要计算\(f(i)\),我们可以用一个更强的结论:

已知有\(n\)个点和\(k\)个联通的点集,第\(i\)个点集大小为\(size[i]\),那么它生成树的个数为:

\[ (\Pi_{i=1} ^ {k} size[i])n^{k-2} \]

\(P\)是长度为\(k-2\)\(prufer\)序列的集合,\(P\)中的每个元素和一个\(k\)个点的生成树一一对应。

但和一般的\(k\)个点生成树不同。这里第\(i\)个点的度数\(+1\),会使答案乘上\(size[i]\)

设第\(i\)个点集出现在\(prufer\)序列中的次数为\(c_i\),那么我们要求的答案就是:

\[ \sum_{p\in P} \Pi_{i=1}^{k} size[i]^{c_i+1}\]

把那个\(+1\)提到外面去,就是考虑计算

\[ \sum_{p\in P} \Pi_{i=1}^{k} size[i]^{c_i} \]

因为\(\sum c_i = k-2\) , 这就相当于走\(k-2\)步,如果走第\(i\)条就产生\(size[i]\)的贡献,求所有方案下的贡献和。

这个就是\((\sum size[i]) ^ {k-2} = n ^ {k - 2}\)

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;

int n, m, a, b;
int fac[N], fav[N], inv[N];

int Qpow(int x, int p) {
    if (p == -1) return inv[x];
    int ans = 1;
    while (p) {
        if (p & 1) ans = 1LL * ans * x % mod;
        x = 1LL * x * x % mod;
        p >>= 1;
    }
    return ans;
}

int C(int x, int y) {
    if (x < y || y < 0) return 0;
    return 1LL * fac[x] * fav[y] % mod * fav[x - y] % mod;
}

void upd(int &x, int y) {
    (x += y) >= mod ? x -= mod : 0;
}

int main() {
    cin >> n >> m >> a >> b;
    int ans = 0;
    fac[0] = fav[0] = 1;
    for (int i = 1; i < N; ++i) {
        inv[i] = Qpow(i, mod - 2);
        fac[i] = 1LL * fac[i - 1] * i % mod;
        fav[i] = 1LL * fav[i - 1] * inv[i] % mod;
    }
    for (int i = 0; i <= n - 2; ++i) {
        int tmp = 1LL * C(n - 2, i) * C(m - 1, i) % mod;
        tmp = 1LL * tmp * fac[i] % mod;
        tmp = 1LL * tmp * Qpow(m, n - 2 - i) % mod;
        tmp = 1LL * tmp * (i + 2) % mod;
        tmp = 1LL * tmp * Qpow(n, n - i - 3) % mod;
        upd(ans, tmp);
    }
    cout << ans << endl;
    return 0;
}
01-08 22:31
查看更多