• 【计数】51nod1677 treecnt


    要将答案看做是小问题的贡献和

    Description

    给定一棵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的连通块个数之类的。

    但是实际上问题可以看做是每一条边对于答案贡献了$si$,答案就是$sum{si}$。

    那么单独的贡献自然应该是选择了横跨这条边的两个点的情况。

    这里就考虑一下问题的反面:选择了不横跨这条边的情况,应该是$C_{u_{size}}^{k}+C_{v_{size}}^{k}$,其中$u_{size}$和$v_{size}$分别表示这条边两边有多少个点。

    于是就愉快地解决这题了。

     1 #include<bits/stdc++.h>
     2 typedef long long ll;
     3 const ll MO = 1000000007;
     4 const int maxn = 100035;
     5 const int maxm = 200035;
     6 
     7 ll fac[maxn],facinv[maxn],ans,sum;
     8 int n,k;
     9 int size[maxn];
    10 int edges[maxm],nxt[maxm],head[maxn],edgeTot;
    11 
    12 int read()
    13 {
    14     char ch = getchar();
    15     int num = 0;
    16     bool fl = 0;
    17     for (; !isdigit(ch); ch = getchar())
    18         if (ch=='-') fl = 1;
    19     for (; isdigit(ch); ch = getchar())
    20         num = (num<<1)+(num<<3)+ch-48;
    21     if (fl) num = -num;
    22     return num;
    23 }
    24 void addedge(int u, int v)
    25 {
    26     edges[++edgeTot] = v, nxt[edgeTot] = head[u], head[u] = edgeTot;
    27     edges[++edgeTot] = u, nxt[edgeTot] = head[v], head[v] = edgeTot;
    28 }
    29 void dfs1(int x, int fa)
    30 {
    31     size[x] = 1;
    32     for (int i=head[x]; i!=-1; i=nxt[i])
    33         if (edges[i]!=fa) dfs1(edges[i], x), size[x] += size[edges[i]];
    34 }
    35 ll c(ll n, ll m){return n < m?0:fac[n]*facinv[n-m]%MO*facinv[m]%MO;}
    36 void dfs2(int x, int fa)
    37 {
    38     for (int i=head[x]; i!=-1; i=nxt[i])
    39     {
    40         int v = edges[i];
    41         if (fa==v) continue;
    42         ans = (ans+sum-c(size[v], k)-c(n-size[v], k))%MO;
    43         dfs2(v, x);
    44     }
    45 }
    46 int main()
    47 {
    48     memset(head, -1, sizeof head);
    49     n = read(), k = read(), fac[1] = facinv[0] = facinv[1] = 1;
    50     for (int i=1; i<n; i++) addedge(read(), read());
    51     for (int i=2; i<=n; i++) facinv[i] = (MO-MO/i)*facinv[MO%i]%MO;
    52     for (int i=2; i<=n; i++)
    53        fac[i] = (fac[i-1]*i)%MO, facinv[i] = facinv[i]*facinv[i-1]%MO;
    54     sum = c(n, k);
    55     dfs1(1, 1);
    56     dfs2(1, 1);
    57     printf("%lld
    ",(ans+MO)%MO);
    58     return 0;
    59 }

    END

  • 相关阅读:
    c++ 从vector扩容看noexcept应用场景
    c++11-17 模板核心知识(十一)—— 编写泛型库需要的基本技术
    动态链接的PLT与GOT
    c++11-17 模板核心知识(十)—— 区分万能引用(universal references)和右值引用
    Golang性能分析与优化
    c++11-17 模板核心知识(九)—— 理解decltype与decltype(auto)
    [LuoguP4808][CCC 2018]平衡树(数论分块+记忆化搜索)(有复杂度证明)
    [NOI2016]区间(线段树+尺取法)
    [BZOJ4316]小C的独立集(仙人掌+树形DP)
    [CTSC2002]灭鼠行动(模拟)
  • 原文地址:https://www.cnblogs.com/antiquality/p/9304816.html
Copyright © 2020-2023  润新知