您的位置:首页 > 其它

51nod-1677 treecnt

2016-12-02 20:19 302 查看
原题链接

1677 treecnt


基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题


 收藏


 关注

给定一棵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


对于每条边只要算出它对答案的贡献次数就可以.假设一条边的两端有kk, n-kk个节点,那么这条边的贡献次数为C(n, k) - C(kk, k) - C(n-kk, k)

#include <cstdio>
#include <cmath>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#define MOD 1000000007
#define maxn 100005
using namespace std;
typedef long long ll;

vector<int> v[maxn];
ll p[maxn], d[maxn];
ll e;
int n, k;
ll solve(ll m){

ll ans = 1, t = MOD - 2;
while(t){
if(t&1)
(ans *= m) %= MOD;
(m *= m) %= MOD;
t >>= 1;
}
return ans;
}
ll f(int kk){

ll mm = p
* d[k] % MOD * d[n-k] % MOD;
if(k <= kk){
mm -= p[kk] * d[k] % MOD * d[kk-k] % MOD;
(mm += MOD) % MOD;
}
if(n - kk >= k){
mm -= p[n-kk] * d[k] % MOD * d[n-kk-k] % MOD;
(mm += MOD) %= MOD;
}
return mm;

}
int dfs(int j, int ff){

int kk = 1;
for(int i = 0; i < v[j].size(); i++){
int h = v[j][i];
if(h != ff){
kk += dfs(h, j);
}
}
(e += f(kk)) %= MOD;
return kk;
}
int main(){

// freopen("in.txt", "r", stdin);
int a, b;
scanf("%d%d", &n, &k);
p[0] = 1;
d[0] = 1;
for(int i = 1; i <= n; i++){
(p[i] = i * p[i-1]) %= MOD;
d[i] = solve(p[i]);
}
for(int i = 0; i < n - 1; i++){
scanf("%d%d", &a, &b);
v[a].push_back(b);
v[b].push_back(a);
}
dfs(1, -1);
printf("%I64d\n", e);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: