【计数】51nod1677 treecnt

要将答案看做是小问题的贡献和

Description

给定一棵n个节点的树,从1到n标号。选择k个点,你需要选择一些边使得这k个点通过选择的边联通,目标是使得选择的边数最少。

现需要计算对于所有选择k个点的情况最小选择边数的总和为多少。
样例解释:


一共有三种可能:(下列配图蓝色点表示选择的点,红色边表示最优方案中的边)

选择点{1,2}:至少要选择第一条边使得1和2联通。

 

选择点{1,3}:至少要选择第二条边使得1和3联通。

 

选择点{2,3}:两条边都要选择才能使2和3联通。

Input

第一行两个数n,k(1<=k<=n<=100000)
接下来n-1行,每行两个数x,y描述一条边(1<=x,y<=n)

Output

一个数,答案对1,000,000,007取模。

Input示例

3 2
1 2
1 3

Output示例

4

题目分析

初看上去好像要结合树形结构做一些麻烦的事情……例如判断树中长度为k的连通块个数之类的。

但是实际上问题可以看做是每一条边对于答案贡献了$si$,答案就是$sum{si}$。

那么单独的贡献自然应该是选择了横跨这条边的两个点的情况。

这里就考虑一下问题的反面:选择了不横跨这条边的情况,应该是$C_{u_{size}}^{k}+C_{v_{size}}^{k}$,其中$u_{size}$和$v_{size}$分别表示这条边两边有多少个点。

于是就愉快地解决这题了。

 1 #include<bits/stdc++.h>
 2 typedef long long ll;
 3 const ll MO = 1000000007;
 4 const int maxn = 100035;
 5 const int maxm = 200035;
 6 
 7 ll fac[maxn],facinv[maxn],ans,sum;
 8 int n,k;
 9 int size[maxn];
10 int edges[maxm],nxt[maxm],head[maxn],edgeTot;
11 
12 int read()
13 {
14     char ch = getchar();
15     int num = 0;
16     bool fl = 0;
17     for (; !isdigit(ch); ch = getchar())
18         if (ch=='-') fl = 1;
19     for (; isdigit(ch); ch = getchar())
20         num = (num<<1)+(num<<3)+ch-48;
21     if (fl) num = -num;
22     return num;
23 }
24 void addedge(int u, int v)
25 {
26     edges[++edgeTot] = v, nxt[edgeTot] = head[u], head[u] = edgeTot;
27     edges[++edgeTot] = u, nxt[edgeTot] = head[v], head[v] = edgeTot;
28 }
29 void dfs1(int x, int fa)
30 {
31     size[x] = 1;
32     for (int i=head[x]; i!=-1; i=nxt[i])
33         if (edges[i]!=fa) dfs1(edges[i], x), size[x] += size[edges[i]];
34 }
35 ll c(ll n, ll m){return n < m?0:fac[n]*facinv[n-m]%MO*facinv[m]%MO;}
36 void dfs2(int x, int fa)
37 {
38     for (int i=head[x]; i!=-1; i=nxt[i])
39     {
40         int v = edges[i];
41         if (fa==v) continue;
42         ans = (ans+sum-c(size[v], k)-c(n-size[v], k))%MO;
43         dfs2(v, x);
44     }
45 }
46 int main()
47 {
48     memset(head, -1, sizeof head);
49     n = read(), k = read(), fac[1] = facinv[0] = facinv[1] = 1;
50     for (int i=1; i<n; i++) addedge(read(), read());
51     for (int i=2; i<=n; i++) facinv[i] = (MO-MO/i)*facinv[MO%i]%MO;
52     for (int i=2; i<=n; i++)
53        fac[i] = (fac[i-1]*i)%MO, facinv[i] = facinv[i]*facinv[i-1]%MO;
54     sum = c(n, k);
55     dfs1(1, 1);
56     dfs2(1, 1);
57     printf("%lld
",(ans+MO)%MO);
58     return 0;
59 }

END

原文地址:https://www.cnblogs.com/antiquality/p/9304816.html