2018.8.5T2(大贪心)

描述
给定一棵 n 个点的边有长度的无根树,小 A 的班里一共有 m 个男生和 m 个女生,他们各自会等概率出现在树上 n 个点中的某一个,(注意同一个点上可能会出现多个人)。

然后小 A 会将他们配对成 m 对男女,设 d i s i 是第 i 对男女在树上的最短距离,小 A 会选择使得 i = 1 m d i s i 最大的配对方案。

现在小 A 想知道,他选择的配对方案中 i = 1 m d i s i 的期望,由于答案可能过大,你只需要输出答案 n 2 m 后对 10 9 + 7 取模的值,这个值显然是一个整数

输入格式
第一行两个正整数 n , m
接下来 n−1 行,每行三个正整数 ( u , v , w ) 表示有一条长度为 w 的边 ( u , v )
输出格式
输出答案 n 2 m 后对 10 9 + 7 取模的值。

样例1
样例输入
3 1
1 2 1
2 3 1
样例输出
8
样例2
见下载文件

补充样例
样例输入
2 2
1 2 1
样例输出
20


惨痛的经历在这里就不再重复了0.0
我们考虑50分 O ( n 3 ) 做法(这里我们把n和m算复杂度都当成n)
好好想想不难想到枚举边,算贡献的做法
我们枚举所有得边,枚举某一遍的男生女生个数
比如说当左边的子树大小为x,边的权值为v时
那么这条边的贡献为:
a = 0 m b = 0 m C m a C m b x a x b ( n x ) m a ( n x ) m b ( m i n ( a , m b ) , m i n ( b , m a ) )
然后组合数还有 x i , ( n x ) i 都可以预处理
那么 O ( n 3 ) 做法就到这了

我们考虑优化,怎么优化到 O ( n 2 ) 或者 O ( n 2 l o g n ) 呢?
solution①
我们把a和b的把部分拆开
也就是 f a = C m a x a ( n x ) m a
然后加一些判断条件后前缀和优化一下即可优化到 O ( n 2 )
是不是觉得很麻烦啊
solution②
实际上我们这里是把枚举边放在外面的
那我们可不可以枚举a和b,考虑边的贡献呢?
化一下式子你会惊讶的
我们把和边有关的提出来,你会发现可以做了
别急,我们看式子:
a = 0 m b = 0 m C m a C m b e d g e E v x a + b ( n x ) 2 m ( a + b ) 你发现后面的式子是一个只和 a + b 有关的式子,也就是我们预处理出来所有边对a+b的贡献
F [ x ] [ a + b ] 式子变为
a = 0 m b = 0 m C m a C m b F [ x ] [ a + b ]
那么也可以优化到 O ( n 2 )
什么??你还嫌麻烦
莫慌莫慌,莫急莫急,下面是我的做法了
solution③
我们考虑贪心
上一个做法的瓶颈在于枚举a和b
我们考虑是否可以直接枚举人
也就是说假如左边 j 个人,右边 2 m j 个人我们能否直接知道这条边会被走过几次呢
我们观察一下 m i n ( a , m b ) + m i n ( b , m a ) m i n ( a + b , 2 m a b ) 直接的关系
假如你数学不好看不出来的话仿佛又遇到了瓶颈
没事,我们有强大的贪心
结论就是上面两个式子相等!
为什么?
我们考虑反证
假如能匹配上的对数小于 m i n ( j , 2 m j )
那么我们把这些已经匹配的人拿掉,意味着剩余人不再匹配,那说明什么?
说明左右所有人同性
矛盾
那么原命题成立
也就是说我们只要枚举总人数再套上面的式子就行了
就是一条边的贡献变为:
j = 0 2 m C 2 m j x j ( n x ) 2 m j v m i n ( j , 2 m j )
同样是 O ( n 2 )
是不是简单又好懂呢

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define rep(i,j,k) for(int i = j;i <= k;++i)
int n , m , linkk[2510] , t;
int son[2510] , dep[2510];
int C[5010][5010];
const int Mod = 1e9+7;
int ans;
struct node{
    int n , x , y , v;
}e[5010];
int read()
{
    int sum = 0;char c = getchar();bool flag = true;
    while( c < '0' || c > '9' ) {if(c == '-') flag = false;c = getchar();}
    while( c >= '0' && c <= '9' ) sum = sum * 10 + c - 48 , c = getchar();
    if(flag)  return sum;
     else return -sum;
}
void insert(int x,int y,int z)
{
    e[++t].y = y;e[t].x = x;e[t].n = linkk[x];e[t].v = z;linkk[x] = t;
    e[++t].y = x;e[t].x = y;e[t].n = linkk[y];e[t].v = z;linkk[y] = t;
    return;
}
void dfs_pre(int x,int fa)
{
    dep[x] = dep[fa] + 1;
    for(int i = linkk[x];i;i = e[i].n)
        if(e[i].y != fa)
        {
            int y = e[i].y;
            dfs_pre(y,x);
            son[x] += son[y];
        }
    son[x]++;
    return;
}
void pre()
{
    C[0][0] = 1;
    rep(i,1,2*m)
    {
        C[i][0] = 1;
        rep(j,1,i-1)
            C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % Mod;
        C[i][i] = 1;
    }
    return;
}
void init()
{
    n = read();m = read();
    rep(i,1,n-1)
    {
        int x = read() , y = read() , z = read();
        insert(x , y , z);
    }
    dfs_pre(1,0);
    pre();
    return;
}
int mi1[5010],mi2[5010];
void mul(int &a,int b)
{
    a = 1ll * a * b % Mod;
    return;
}
void work()
{
    mi1[0] = mi2[0] = 1;
    rep(i,1,n-1)
    {
        int x = e[2 * i - 1].x , y = e[2 * i - 1].y , v = e[2 * i - 1].v;
        if(v == 0) continue;
        if(dep[x] > dep[y]) swap(x,y);
        rep(i,1,2*m) mi1[i] = (1ll*mi1[i-1] * son[y]) % Mod;//son^y
        rep(i,1,2*m) mi2[i] = (1ll*mi2[i-1] * (n-son[y])) % Mod;
        rep(a,0,2*m)
        {
            int now = C[2*m][a];
            mul(now,mi1[a]);mul(now,mi2[2*m - a]);mul(now,min(a,2*m-a));mul(now,v);
            ans = (ans + now) % Mod;
        }
    }
    printf("%d\n",ans);
    return;
}
int main()
{
    init();
    work();
    return 0;
} 

猜你喜欢

转载自blog.csdn.net/a1035719430/article/details/81435812