Codeforces 1109D sasha and interesting fact from graph theory

题目链接: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_{pin P} Pi_{i=1}^{k} size[i]^{c_i+1} ]

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

[sum_{pin 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;
}
原文地址:https://www.cnblogs.com/Vexoben/p/11728699.html