【计数】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\n",(ans+MO)%MO); 58 return 0; 59 }
END