51Nod - 1677 treecnt
Discription
给定一棵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取模。
Sample Input
3 2
1 2
1 3
Sample Output
4
直接考虑每条边在选哪些点的时候会被选就行了。
#include<bits/stdc++.h> #define ll long long using namespace std; const int maxn=100000,ha=1000000007; int jc[maxn+5],ni[maxn+5],n,m,siz[maxn+5],T,ans; int hd[maxn+5],ne[maxn*2+5],to[maxn*2+5],num; inline void addline(int x,int y){ to[++num]=y,ne[num]=hd[x],hd[x]=num;} inline int add(int x,int y){ x+=y; return x>=ha?x-ha:x;} inline int ksm(int x,int y){ int an=1; for(;y;y>>=1,x=x*(ll)x%ha) if(y&1) an=an*(ll)x%ha; return an;} inline int C(int x,int y){ return x<y?0:jc[x]*(ll)ni[y]%ha*(ll)ni[x-y]%ha;} inline void init(){ jc[0]=1; for(int i=1;i<=maxn;i++) jc[i]=jc[i-1]*(ll)i%ha; ni[maxn]=ksm(jc[maxn],ha-2); for(int i=maxn;i;i--) ni[i-1]=ni[i]*(ll)i%ha; } void dfs(int x,int fa){ siz[x]=1; for(int i=hd[x];i;i=ne[i]) if(to[i]!=fa) dfs(to[i],x),siz[x]+=siz[to[i]]; if(fa) ans=add(ans,add(T,ha-add(C(siz[x],m),C(n-siz[x],m)))); } int main(){ int uu,vv; init(); scanf("%d%d",&n,&m),T=C(n,m); for(int i=1;i<n;i++){ scanf("%d%d",&uu,&vv); addline(uu,vv),addline(vv,uu); } dfs(1,0),printf("%d\n",ans); return 0; }
我爱学习,学习使我快乐