51nod 1677

考虑树上的每条边对答案的贡献
--- x ----y ---
若 x 左边有 a2 个点,y 的右边有 a3 个点
那么改边对答案的贡献为 C(n, k) - C(a2, k) - C(a3, k)
快速幂求逆元计算组合数
注意:计算C(n, m)时, 若 m > n 返回 0

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>

#define gc getchar()
#define ULL unsigned long long
#define LL long long

inline int read() {
    int x = 0; char c = gc;
    while(c < ‘0‘ || c > ‘9‘) c = gc;
    while(c >= ‘0‘ && c <= ‘9‘) x = x * 10 + c - ‘0‘, c = gc;
    return x;
}

const int N = 1e5 + 10, Mod = 1e9 + 7;

int head[N], cnt;
struct Node {int u, v, nxt;} G[N << 1];
int size[N], deep[N];
int n, k;
ULL fac[N];

inline void Add(int u, int v) {G[++ cnt].v = v; G[cnt].nxt = head[u]; head[u] = cnt;}

void Dfs(int u, int fa, int dep) {
    size[u] = 1, deep[u] = dep;
    for(int i = head[u]; ~ i; i = G[i].nxt) {
        int v = G[i].v;
        if(v == fa) continue;
        Dfs(v, u, dep + 1);
        size[u] += size[v];
    }
}

ULL Ksm(ULL a, ULL b) {
    ULL ret = 1;
    while(b) {
        if(b & 1) ret = (ret * a) % Mod;
        a = (a * a) % Mod;
        b >>= 1;
    }
    return ret;
}

Node E[N];

inline ULL C(int n_, int m_) {
    if(m_ > n_) return 0;
    ULL x = (fac[m_] * fac[n_ - m_]) % Mod;
    return (ULL) (fac[n_] * Ksm(x, Mod - 2)) % Mod;
}

int main() {
    n = read(), k = read();
    fac[1] = fac[0] = 1;
    for(int i = 1; i <= n; i ++) fac[i] = (fac[i - 1] * i) % Mod;
    for(int i = 1; i <= n; i ++) head[i] = -1;
    for(int i = 1; i < n; i ++) {
        int u = read(), v = read(); Add(u, v), Add(v, u);
        E[i].u = u, E[i].v = v;
    }
    Dfs(1, 0, 1);
    LL a1 = C(n, k);
    LL Answer = 0;
    for(int i = 1; i < n; i ++) {
        int x = E[i].u, y = E[i].v;
        if(deep[x] < deep[y]) std:: swap(x, y);
        int siz1 = size[x], siz2 = n - siz1;
        LL  a2 = C(siz1, k), a3 = C(siz2, k);
        Answer = (Answer + (a1 - a2 - a3) % Mod) % Mod;
    }
    while(Answer < 0) Answer += Mod;
    std:: cout << Answer;
    return 0;
}

原文地址:https://www.cnblogs.com/shandongs1/p/9462340.html

时间: 2024-10-06 21:44:57

51nod 1677的相关文章

51Nod 1677 treecnt 【树形dp+组合数学+逆元】

51Nod 1677  treecnt Description:给定一棵n个节点的树,从1到n标号.选择k个点,你需要选择一些边使得这k个点通过选择的边联通,目标是使得选择的边数最少. 现需要计算对于所有选择k个点的情况最小选择边数的总和为多少. 样例解释: 一共有三种可能:(下列配图蓝色点表示选择的点,红色边表示最优方案中的边) 选择点{1,2}:至少要选择第一条边使得1和2联通.   选择点{1,3}:至少要选择第二条边使得1和3联通. 选择点{2,3}:两条边都要选择才能使2和3联通. I

51Nod 1677 treecnt

一道比较基础的计数题,还是一个常用的单独计算贡献的例子. 首先看题目和范围,暴力枚举肯定是不可行的,而且\(O(n\ logn)\)的算法貌似很难写. 那我们就来想\(O(n)\)的吧,我们单独考虑每一条边的贡献,我们注意到一个重要的性质: 树上任意两点间的最短路径都是唯一确定的. 这个常识吧,所以我们只需要考虑每一条边两边的点在计算时会经过这条边多少次. 我们枚举每一条边,然后可以这样考虑这一条边: 我们设一边有\(x\)个点,另一边有\(y\)个点,很明显\(x+y=n\) 然后我们考虑有多

51nod 1201 整数划分(dp)

题目链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1201 题解:显然是一道dp,不妨设dp[i][j]表示数字i分成j个一共有几种分法. 那么转移方程式为: dp[i][j] = dp[i - 1][j] + dp[i - 1][j - 1] 表示将i - 1划分为j个数,然后j个数都+1 还是不重复,将i - 1划分为j - 1个数,然后j - 1个数都+1,再加上1这个数. 然后就是j的范围要知道1+2+

51nod 1138 连续整数的和(数学)

题目描述: http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1138 给出一个正整数N,将N写为若干个连续数字和的形式(长度 >= 2).例如N = 15,可以写为1 + 2 + 3 + 4 + 5,也可以写为4 + 5 + 6,或7 + 8.如果不能写为若干个连续整数的和,则输出No Solution. Input 输入1个数N(3 <= N <= 10^9). OutPut 输出连续整数中的第1个数,如果有多

51nod 1463 找朋友(线段树+离线处理)

http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1463 题意: 思路: 好题! 先对所有查询进行离线处理,按照右区间排序,因为k一共最多只有10个,所有在该区间内的B数组,每次枚举K值,通过这样的方式来得到另外一个B值.但是这样得到的B值它在B数组中的位置必须在当前数的左边.如下图:(j为当前数在B数组中的位置,pos为计算得到的另一个B值在数组中的位置) 这两个数的和记录在pos中,这里pos的位置必须在j的左边,假

51nod 1437

http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1437 1437 迈克步 题目来源: CodeForces 基准时间限制:1 秒 空间限制:131072 KB 分值: 80 难度:5级算法题 收藏 关注 有n只熊.他们站成一排队伍,从左到右依次1到n编号.第i只熊的高度是ai. 一组熊指的队伍中连续的一个子段.组的大小就是熊的数目.而组的力量就是这一组熊中最小的高度. 迈克想知道对于所有的组大小为x(1 ≤ x ≤ n

51nod 1272 思维/线段树

http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1272 1272 最大距离 题目来源: Codility 基准时间限制:1 秒 空间限制:131072 KB 分值: 20 难度:3级算法题 收藏 关注 给出一个长度为N的整数数组A,对于每一个数组元素,如果他后面存在大于等于该元素的数,则这两个数可以组成一对.每个元素和自己也可以组成一对.例如:{5, 3, 6, 3, 4, 2},可以组成11对,如下(数字为下标):

51nod 1406 位运算/dp

http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1406 1406 与查询 题目来源: CodeForces 基准时间限制:2 秒 空间限制:131072 KB 分值: 80 难度:5级算法题 收藏 关注 有n个整数.输出他之中和x相与之后结果为x的有多少个.x从0到1,000,000 Input 第一行输入一个整数n.(1<=n<=1,000,000). 第二行有n个整数a[0],a[1],a[2],...a[n-1

51nod 1307 绳子与重物(并查集水了一发)

http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1307 题意: 思路: 可以直接二分答案,然后dfs. 因为标签是并查集,所以我考虑了一下并查集,利用并查集不断向上回溯加负重,居然过了,只能说数据有点水. 1 #include<iostream> 2 #include<algorithm> 3 #include<cstring> 4 #include<cstdio> 5 #incl