zoukankan      html  css  js  c++  java
  • 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;
    }
    
    
    
     
  • 相关阅读:
    ElasticSearch入门 第一篇:Windows下安装ElasticSearch
    Elasticsearch+Logstash+Kibana教程
    MySQL组合索引最左匹配原则
    mysql 有哪些索引
    MySQL配置优化
    MySQL分区和分表
    MySQL优化
    MySQL锁详解
    MySQL各存储引擎
    MySQL索引类型
  • 原文地址:https://www.cnblogs.com/zzyh/p/7644323.html
Copyright © 2011-2022 走看看