51Nod1677 - treecnt(算法马拉松20(告别美国大选及卡斯特罗)- A)
2016-11-29 20:34
260 查看
Problem : treecnt
Description :
给定一棵n个节点的树,从1到n标号。选择k个点,你需要选择一些边使得这k个点通过选择的边联通,目标是使得选择的边数最少。
现需要计算对于所有选择k个点的情况最小选择边数的总和为多少。
Solution :
组合数 + 贡献 + dfs。 一开始我以为是树形DP,但是一看数据范围,太大了,做不了,于是我就想,如果不要最小这两个字,那么结果就是(kn)∗(n−1)。那么现在这个结果肯定多了,于是我们剔除那些不合法的情况就好了,考虑每条边,如果这条边要被选择,那么这条边的两边一定有点!那么不合法的情况就是点都集中在边的一边,于是对于每条边要被用到的次数就可以算出来了:(kn)−(kx)−(kn−x)x表示集中在一条边的一边的个数。那么现在这个问题就转化成求一棵树上每颗子树上有多少个子孙,这个用dfs可以搞。由于这个题输入的方式没有指明谁是父亲谁是儿子,那么我们就按图的存储方式来存树!
Code(C++) :
Description :
给定一棵n个节点的树,从1到n标号。选择k个点,你需要选择一些边使得这k个点通过选择的边联通,目标是使得选择的边数最少。
现需要计算对于所有选择k个点的情况最小选择边数的总和为多少。
Solution :
组合数 + 贡献 + dfs。 一开始我以为是树形DP,但是一看数据范围,太大了,做不了,于是我就想,如果不要最小这两个字,那么结果就是(kn)∗(n−1)。那么现在这个结果肯定多了,于是我们剔除那些不合法的情况就好了,考虑每条边,如果这条边要被选择,那么这条边的两边一定有点!那么不合法的情况就是点都集中在边的一边,于是对于每条边要被用到的次数就可以算出来了:(kn)−(kx)−(kn−x)x表示集中在一条边的一边的个数。那么现在这个问题就转化成求一棵树上每颗子树上有多少个子孙,这个用dfs可以搞。由于这个题输入的方式没有指明谁是父亲谁是儿子,那么我们就按图的存储方式来存树!
Code(C++) :
#include <stdio.h> #include <string.h> #include <iostream> using namespace std; #define MAXN 100005 typedef struct tagNode { int des, next; tagNode() {} tagNode(int des, int next) : des(des), next(next) {} }Node; typedef long long LL; LL fac[MAXN]; int head[MAXN]; Node nodes[3 * MAXN]; bool used[MAXN]; int n, k; const LL mod = 1e9 + 7; LL ans; void get_fac() { fac[0] = 1; for (int i = 1; i < MAXN; i++) fac[i] = fac[i - 1] * i % mod; } void add_edge(int p, int c, int i) { nodes[i] = Node(c, head[p]); head[p] = i; } LL inv(LL x, LL n) { n = n - 2; LL ret = 1, tmp = x; while (n) { if (n & 1) ret = ret * x % mod; x = x * x % mod; n >>= 1; } return ret; } LL C(LL n, LL m) { LL ret = 1; while (n && m) { LL nn = n % mod; LL mm = m % mod; if (nn < mm) return 0; ret = (ret * fac[nn] % mod) * inv(fac[nn - mm] * fac[mm] % mod, mod) % mod; n /= mod; m /= mod; } return ret; } int dfs(int x) { used[x] = true; int sum = 1; for (int i = head[x]; i + 1; i = nodes[i].next) if (false == used[nodes[i].des]) sum += dfs(nodes[i].des); if (x != 1) ans = (ans + C(n, k) - C(sum, k) -C(n - sum, k) + mod) % mod ; return sum; } int main() { get_fac(); while (~scanf("%d%d", &n, &k)) { memset(head, -1, sizeof(head)); memset(used, false, sizeof(used)); for (int i = 0; i < n - 1; i++) { int p, c; scanf("%d%d", &p, &c); add_edge(p, c, i); add_edge(c, p, i + n - 1); } ans = 0; dfs(1); printf("%lld\n", ans); } return 0; }
相关文章推荐
- treecnt 算法马拉松20(告别美国大选及卡斯特罗)
- 51NOD 1836 战忽局的手段(矩阵乘法加速+__float128精度问题+概率期望)——算法马拉松20(告别美国大选及卡斯特罗)
- 51nod1577 异或凑数(算法马拉松20) 特殊的线性基构造方法
- 51NOD 1537 分解(矩阵快速幂)——算法马拉松17(告别奥运)
- 51NOD 1661 黑板上的游戏(博弈 找规律)——算法马拉松17(告别奥运)
- 51nod算法马拉松20
- 算法马拉松29 美丽的集合(启发式合并/bitset)
- [20] 鼓状物(Drum)图形的生成算法
- [算法]求1+2!+3!+...+20!的和
- [暴力 乱搞] 51Nod 1501 算法马拉松19 D 石头剪刀布威力加强版
- 【51nod】算法马拉松19 总结
- 算法题:背包最大承重为20,算出装满水果后价格最高的组合,水果不能重复。
- 美国总统大选,以平局开始, 以奥巴马胜利告终
- 程序员面试题精选100题(20)-最长公共子串[算法]
- 「51nod 算法马拉松31C」彩色树
- 51nod 算法马拉松11 D 计算
- [FWT] 51nod 算法马拉松26 A. A国的贸易
- 51 nod 算法马拉松28 栈
- 51NOD 算法马拉松12
- 15算法课程 20. Valid Parentheses