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;
}
 
   

 

 

转载于:https://www.cnblogs.com/zzyh/p/7644323.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值