HDU 6382 树形DP

题目链接

题意:

一颗有根边带权树,每个节点去往其子节点的概率为(对应边权/200000),对于从某一节点前往其子节点边的边权间可以调换,求最多调换m次从根节点到任意叶子节点的概率,输出时以mod(1e9+7)意义下的整数输出

思路:

对于维护根到某一叶子节点的概率时,维护路径上每一条边的一些信息:

定义利用率 = 节点向叶子节点走去的最大边权/路径上对应边的边权

定义原概率 = 路径上对应边的边权/200000

将这两项相乘可以发现(路径上对应边的边权)会被消去,并且原概率是一个定值无法改变,所以我们只需使得利用率尽量大即可,所以我们只要维护出路径上利用率最大的那(<=m)次调换即可

维护利用率时需要注意(路径上对应边的边权)可能为0,此时因为路径上对应边的边权==0所以利用率==无限大,原概率==0,乘积==不知道(+_+)?,因此但遇到0时,我们需要特殊处理一下:记录下路径上0的出现次数,并将利用率乘上(节点向叶子节点走去的最大边权/200000)原概率不变。为什么原概率不变,因为本来在原概率里面该乘上的东西变化成了记录出现次数了。有一个明显的结论:当0的出现次数>m时到叶节点的概率为0,反之等于利用率*原概率

现在又有一个问题如果遍历每一个叶节点然后再计算叶节点的概率不是还会超时嘛,这怎么解决啊,所以我们需要考虑一次遍历整颗树同时得到所有叶节点的答案,这个如何实现呢?考虑树DP式树遍历,用multiset维护前m大值,变量存储前m大的结果和0的出现次数,细节操作实现看代码

C++代码:

#include <bits/stdc++.h>
using namespace std;
const int mod  = 1e9+7;
const int maxn = 100010;
const int maxm = 200010;
const int num  = 200000;
int Max( int a , int b ){ return a>b?a:b; }

int n,m,tol,head[maxn],maxx[maxn],ans[maxn],inv[maxm];
struct edge
{
    int to,cost,next;
}es[maxn];
void addedge( int u , int v , int w )
{
    maxx[u] = Max( maxx[u] , w );
    es[tol].to = v;
    es[tol].cost = w;
    es[tol].next = head[u];
    head[u] =  tol++;
}
struct node
{
    int a,b;
    friend bool operator<( const node&a , const node&b )
    {
        return 1LL*a.a*b.b<1LL*a.b*b.a;
    }
};
multiset<node>S;
int sum,can,cnt;
void dfs( int u , int f )
{
    for ( int i=head[u] ; i!=-1 ; i=es[i].next )
    {
        int v = es[i].to,w = es[i].cost,a = -1,b = -1;
        if ( w==0 )
        {
            sum = 1LL*sum*maxx[u]%mod*inv[num]%mod;
            cnt++;
            node p;
            if ( !S.empty()&&S.size()+cnt>m )
            {
                p = *S.begin();
                S.erase(S.begin());
                a = p.a;
                b = p.b;
                sum = 1LL*sum*b%mod*inv[a]%mod;
            }
            dfs( v , u );
            cnt--;
            if ( a!=-1&&b!=-1 )
            {
                p.a = a;
                p.b = b;
                S.insert(p);
                sum = 1LL*sum*a%mod*inv[b]%mod;
            }
            sum = 1LL*sum*num%mod*inv[maxx[u]]%mod;
        }
        else
        {
            sum = 1LL*sum*maxx[u]%mod*inv[w]%mod;
            can = 1LL*can*w%mod*inv[num]%mod;
            node p; p.a = maxx[u]; p.b = w;
            S.insert(p);
            if ( !S.empty()&&S.size()+cnt>m )
            {
                p = *S.begin();
                S.erase(S.begin());
                a = p.a;
                b = p.b;
                sum = 1LL*sum*b%mod*inv[a]%mod;
            }
            dfs( v , u );
            if ( a!=-1&&b!=-1 )
            {
                p.a = a;
                p.b = b;
                S.insert(p);
                sum = 1LL*sum*a%mod*inv[b]%mod;
            }
            p.a = maxx[u];
            p.b = w;
            multiset<node>::iterator it = S.lower_bound( p );
            S.erase(it);
            can = 1LL*can*num%mod*inv[w]%mod;
            sum = 1LL*sum*w%mod*inv[maxx[u]]%mod;
        }
    }
    if ( maxx[u]==-1 )
    {
        if ( cnt>m )
            ans[u] = 0;
        else
            ans[u] = 1LL*sum*can%mod;
    }
}
int main()
{
    inv[1] = 1;
    for ( int i=2 ; i<maxm ; i++ )
        inv[i] = 1LL*inv[mod%i]*(mod-mod/i)%mod;
    for ( int T ; scanf ( "%d" , &T )==1 ; )
    {
        for ( int cas=1 ; cas<=T ; cas++ )
        {
            tol = 0,S.clear();
            memset ( head , -1 , sizeof(head) );
            memset ( maxx , -1 , sizeof(maxx) );
            memset ( ans , -1 , sizeof(ans) );
            scanf ( "%d%d" , &n , &m );
            for ( int i=1 ; i<n ; i++ )
            {
                int u,v,w;
                scanf ( "%d%d%d" , &u , &v , &w );
                addedge ( u , v , w );
            }
            sum = 1,can = 1,cnt = 0;
            dfs ( 0 , -1 );
            for ( int i=0 ; i<n ; i++ )
                if ( ans[i]!=-1 )
                    printf ( "%d\n" , ans[i] );
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Game_Acm/article/details/81776544