2017 CDQZ 联训 Day9 例题 treecnt

-> 题目连接 <-

【题目大意】

给定一棵n个节点的树,从1到n标号。选择k个点,你需要选择一些边使得这k个点通过选择的边联通,目标是使得选择的边数最少。

现需要计算对于所有选择k个点的情况最小选择边数的总和为多少。

1<=k<=n<=100000

【解题思路】

因为这道题要求统计所有k个点的选取方案,暴力枚举肯定不现实,这种情况下一般可以考虑每条边的贡献。

树上的每条边都是桥,所以当且仅当选取的k个点在某条边连接的两个连通块中都有接点,删掉这条边才是合理而且必要的。考虑补集,当这k个点只存在于在某条边连接的两个连通块中的一个中,这条边就不会对答案造成贡献。

假设这条边连接的两个连通块的大小分别为siz和N-siz。那么这条边的贡献就为CknCksizCkNsiz 。把所有边的贡献加起来即为最终答案。

【代码】

第一次提交之后WA了一大堆点,后来懒得查错了直接写了个#define int long long然后再把int main()改成signed main()就AC了。。这真是个好方法

#include<cstdio>
#include<cstdlib>
#include<vector>
#include<algorithm>
using namespace std;
#define int long long
const int maxn=100000+10,MOD=1000000000+7;
typedef long long LLint;
namespace combine{
    int fac[maxn];
    void exgcd(int a,int b,int& x,int& y){
        if(b==0){x=1;y=0;return;}
        int x0,y0;exgcd(b,a%b,x0,y0);
        x=y0;y=x0-a/b*y0;
    }
    int inv(int a,int p=MOD){
        int x,y;exgcd(a,p,x,y);
        return (x%p+p)%p;
    }
    void init(int n){
        fac[0]=1;
        for(int i=1;i<=n;i++){
            fac[i]=(fac[i-1]*i)%MOD;
        }
    }
    LLint C(int n,int m){
        if(n==m || m==0)return 1;
        if(n<m)return 0;
        return ((long long)fac[n]*inv(fac[n-m])%MOD)
        *inv(fac[m])%MOD;
    }
}
namespace tree{
    int fa[maxn],siz[maxn],ans=0,n,k;
    vector<int>G[maxn];
    inline void addedge(int f,int t){
        G[f].push_back(t);
        G[t].push_back(f);
    }
    int dfs(int x,int f){
        #define C combine::C
        fa[x]=f;siz[x]=1;
        for(int i=0;i<G[x].size();i++){
            int u=G[x][i];
            if(u==f)continue;
            int ssiz=dfs(u,x);
            siz[x]+=ssiz;
            ans=((((ans+C(n,k))%MOD-C(ssiz,k))%MOD
            -C(n-ssiz,k))%MOD+MOD)%MOD;
        }
        return siz[x];
        #undef C
    }
}
#include<cctype>
int geti(){
    int ans=0,flag=0;char c=getchar();
    while(!isdigit(c)){flag|=c=='-';c=getchar();}
    while( isdigit(c)){ans=ans*10+c-'0';c=getchar();}
    return flag?-ans:ans;
}
void puti(int x){
    if(x<0)x=-x,putchar('-');
    if(x>9)puti(x/10);   putchar(x%10+'0');
}
signed main(){
    #define n tree::n
    #define k tree::k
    n=geti(),k=geti();
    combine::init(n);
    for(int i=1;i<n;i++){
        int f=geti(),t=geti();
        tree::addedge(f,t);
    }
    tree::dfs(1,-1);
    printf("%d\n",tree::ans);
    #undef n
    #undef k
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章