西南科技大学院赛 节日灯花 题解
题目
给定有标号有根树,求儿子节点数相同的树的个数
知识铺垫
prufer序列
- 定义:一个带有标记的无根树,对应一个prufer序列
- n个节点的树对应prufer序列长度为n - 2
树->prufer
- 找到度为一的标号最小的节点,将它删除,并把它所连接的节点加入序列中
- 直到剩余两个点停止
- 一个点加入prufer时,说明它有一条连向叶子节点的边,加入后,那条边会删除,度数减一,若不存在连向其他叶子节点的边,那么该点就不会再次出现在prufer序列中,故点在prufer序列出现次数为度数-1
prufer->树
- 维护一个未出现的集合S,从前遍往后遍历prufer序列,找到集合S中最小的未在prufer序列中出现过的元素,和prufer序列首项添加一条边,并删除两元素,重复n - 2次
应用
- n n n个带标记的无根树方案为 n ( n − 2 ) n ^{(n - 2)} n(n−2)
- n n n个带标记的有根数方案为 n ( n − 1 ) n^{(n - 1)} n(n−1)
- n n n个节点,第 i i i个节点度为 d i d_i di,方案数为 ( n − 2 ) ! ∏ ( d i − 1 ) ! \frac{(n-2)!}{\prod({d_i-1})!} ∏(di−1)!(n−2)!
题解:
- 首先我们知道每个节点的度为 d i d_i di
- 可以推出根节点儿子节点个数 s o n i = d i son_i =d_i soni=di,非根节点 s o n i = d i − 1 son_i=d_i-1 soni=di−1
- 因为可以通过排序,那么只要满足排完序列 s o n i son_i soni相等即可
- 统计 c n t i cnt_i cnti为 s o n i son_i soni的个数
- 根节点的选法有 s u m i = ∑ c n t i ∗ s o n i sum_i =\sum{cnt_i}*son_i sumi=∑cnti∗soni
- 选完根节点后,还有 n − 1 n-1 n−1个点,剩余点选剩余的 s o n i son_i soni根据多重排列可得方案数为 ( n − 1 ) ! ∏ c n t i \frac{(n-1)!}{\prod{cnti}} ∏cnti(n−1)!
- 故最后总方案数为 s u m i ∗ ( n − 2 ) ! ∏ ( s o n i ) ! ∗ ( n − 1 ) ! ∏ c n t i sum_i*\frac{(n-2)!}{\prod({son_i})!}*\frac{(n-1)!}{\prod{cnti}} sumi∗∏(soni)!(n−2)!∗∏cnti(n−1)!
代码:
#include <bits/stdc++.h>
#define rep(i,a,b) for (int i = a; i <= (int)(b); i ++ )
#define x first
#define y second
using namespace std;
using ll = long long;
const int maxn = 1e5 + 10, mod = 1e9 + 7;
ll din[maxn], cnt[maxn];
ll fac[maxn], invfac[maxn], inv[maxn];
ll qmi(ll a, ll b)
{
ll res = 1;
while(b)
{
if(b & 1) res = (res * a) % mod;
b >>= 1;
a = (a * a) % mod;
}
return res;
}
void get()
{
fac[0] = 1;
invfac[0] = 1;
rep(i,1,maxn-1) fac[i] = fac[i-1] * i % mod;
rep(i,1,maxn-1) invfac[i] = qmi(fac[i], mod-2);
}
typedef pair<int,int> pii;
pii a[maxn];
void init()
{
memset(din,0,sizeof din);
memset(cnt,0,sizeof cnt);
memset(a,0,sizeof a);
}
void solve()
{
init();
int n;
std::cin >> n;
rep(i,1,n-1)
{
int u, v;
scanf("%d%d", &u, &v);
din[u]++,din[v]++;
}
rep(i,2,n) din[i] --;
rep(i,1,n)
{
cnt[din[i]]++;
}
ll idx = 0;
rep(i,0,n)
{
if(cnt[i]) a[++idx] = make_pair(i, cnt[i]);
}
ll ans = fac[n-1] * fac[n-2] % mod;
ll sum = 0;
rep(i,1,idx)
{
sum = (sum + 1ll * a[i].x * a[i].y % mod) % mod;
ans = (ans * invfac[a[i].y]) % mod;
rep(j,1,a[i].y) ans = (ans * invfac[a[i].x]) % mod;
}
std::cout << ans * sum % mod << "\n";
}
int main()
{
get();
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif
int t;
std::cin >> t;
while(t -- )
{
solve();
}
return 0;
}