51nod 1677 treecnt
Posted 小时のblog
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了51nod 1677 treecnt相关的知识,希望对你有一定的参考价值。
给定一棵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个点,用最少的边把这k个点联通,将边数计入答案,求所有
情况累计的答案和。
题解:卢卡斯定理+统计
考虑每条边对答案的贡献。
当切去某一条边时,树会分成两部分。只有当选中的k个点完全在两部分中的
一个时,这条边对这k个点没有贡献。总共的情况数为C(n,k),减去没有贡献
的情况C(size[x],k),和C(n-size[x],k)就是这条边出现的次数。其中
size[x]为以切去的这条边的深度较深的端点为根的子树的个数。组合数用卢卡
斯定理处理,预处理逆元。
代码:
#include<iostream> #include<cstdio> #include<cstring> #define LL long long #define maxn 100008 #define mod 1000000007 using namespace std; LL n,k,sumedge,ans; LL head[maxn],size[maxn],f[maxn],inv[maxn]; struct Edge{ int x,y,nxt; Edge(int x=0,int y=0,int nxt=0): x(x),y(y),nxt(nxt){} }edge[maxn<<1]; void add(int x,int y){ edge[++sumedge]=Edge(x,y,head[x]); head[x]=sumedge; } LL ksm(LL x,LL y){ LL ret=1; while(y){ if(y&1) ret=ret*x%mod; x=x*x%mod; y>>=1; } return ret; } void pre(){ f[0]=inv[0]=1; for(int i=1;i<=maxn;i++){ f[i]=(f[i-1]*i)%mod; inv[i]=ksm(f[i],mod-2); } } LL Lucas(LL n,LL m){ if(m>n)return 0; if(m==n)return 1; return f[n]*inv[m]%mod*inv[n-m]%mod; } void dfs(int x,int fa){ size[x]=1; for(int i=head[x];i;i=edge[i].nxt){ int v=edge[i].y; if(v==fa)continue; dfs(v,x); size[x]+=size[v]; } ans=(ans+Lucas(n,k)-Lucas(size[x],k)-Lucas(n-size[x],k)+mod)%mod; } int main(){ scanf("%lld%lld",&n,&k); for(int i=1;i<n;i++){ int x,y; scanf("%d%d",&x,&y); add(x,y);add(y,x); } pre();dfs(1,-1); cout<<ans<<endl; return 0; }
以上是关于51nod 1677 treecnt的主要内容,如果未能解决你的问题,请参考以下文章