zoukankan      html  css  js  c++  java
  • HDU 4661 Message Passing ( 树DP + 推公式 )

    参考了:

    http://www.cnblogs.com/zhsl/archive/2013/08/10/3250755.html

    http://blog.csdn.net/chaobaimingtian/article/details/9852761

    题意:一个有n个节点的树,每个节点存有一份独一无二的信息,要求用最小的步数,把每个节点的信息共享给所有的节点。一个节点把自己所包含的所有信息传递给相邻的一个节点为一步。

    题目不是求最小的步数,而是问最小的步数下,信息传递的方法有多少种。

    分析:

    1. 最小步数:所有节点把信息传给同一个节点,再由这个节点传给其他节点。因此最小步数为树边数的2倍,即2*(n-1)。我们把这个节点称为信息交换的中心节点。
    2. 拓扑排序数:从根节点开始,沿着每个边依次遍历每个点的不同走法。比如对于树:n=4,边为(1,2)(1,3)(2,4),边的编号依次为1,2,3,设根为1,那么不同的遍历方案有(这里写的是边的序号):(1,3,2)(1,2,3)(2,1,3)三种。我们说以1为根的拓扑排序数为3。
    3. 假设所有节点把信息传给中心节点的拓扑排序数为X,那么再由这个节点传给其他节点的拓扑排序数也为X,总的方法数就是X2
    4. 枚举所有的中心节点Xi, 总方法数即为ans = sum( Xi2 ), 1 <= i<= N;
    5. 求每个节点的拓扑排序数:DFS一次,记录dp[u], cnt[u]。dp[u]为以u为根节点的子树的拓扑排序数,cnt[u]为以u为根节点的子树的节点的个数。假设v1,v2为u的两个子树,那么v1, v2合并后的拓扑排序数为:sum = dp[v1]*dp[v2]*C( cnt[v1]+cnt[v2], cnt[v1]);(C为组合数公式)对于u的所有儿子,可以采用两两合并的方法。
    6. 求以u为中心节点的拓扑排序数dp[u](即u为整棵树的根节点):再次DFS一遍。

    设u的父亲为fa,因为DFS是先根序遍历,因此我们在求以u为中心节点的拓扑排序数dp[u]之前,已经先将以fa为中心节点的拓扑排序数dp[fa]求了出来,因此下面我们可以直接使用这个值。

    我们将fa中除去子树u的所有子树合并成一个子树t,根据上面的式子:

     

    #pragma comment(linker,"/STACK:102400000,102400000")
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    
    #define LL long long int
    
    using namespace std;
    
    const int MAXN = 1000010;
    const LL MOD = 1000000007;
    
    struct Edge
    {
        int v;
        int next;
    };
    
    int head[MAXN];
    Edge D[ MAXN << 1 ];
    int N, ans;
    int EdgeN;
    LL cnt[MAXN];
    LL dp[MAXN];
    LL fac[MAXN];
    LL rev[MAXN];
    
    void AddEdge( int u, int v )
    {
        D[EdgeN].v = v;
        D[EdgeN].next = head[u];
        head[u] = EdgeN++;
        return;
    }
    
    //求逆元模板
    void ExGcd( LL a, LL b, LL& d, LL& x, LL& y )
    {
        if ( !b ) { d = a, x = 1, y = 0; }
        else
        {
            ExGcd( b, a % b, d, y, x );
            y -= x * ( a / b );
        }
        return;
    }
    
    LL GetInverse( LL num )
    {
        LL d, x, y;
        ExGcd( num, MOD, d, x, y );
        return ( x % MOD + MOD ) % MOD;
    }
    
    //预处理出所有阶乘和逆元
    void init()
    {
        fac[0] = 1;
        for ( int i = 1; i < MAXN; ++i )
            fac[i] = ( fac[i - 1] * i ) % MOD;
    
        for ( int i = 1; i < MAXN; ++i )
            rev[i] = GetInverse( fac[i] );
    
        return;
    }
    
    //第一次DFS,求出以cur为根的子树的节点个数cnt[u]和拓扑排序数dp[cur]
    void DFS1( int cur, int fa )
    {
        cnt[cur] = dp[cur] = 1;
        for ( int i = head[cur]; i != -1; i = D[i].next )
        {
            if ( D[i].v == fa ) continue;
            DFS1( D[i].v, cur );
            cnt[cur] += cnt[ D[i].v ];
            dp[cur] = ( (dp[cur]*dp[ D[i].v ])%MOD * rev[cnt[D[i].v]] )%MOD;
        }
        dp[cur] = ( dp[cur] * fac[ cnt[cur]-1 ] ) % MOD;
        return;
    }
    
    //第二次DFS,求出以cur为中心的拓扑排序数dp[cur]
    void DFS2( int cur, int fa )
    {
        if ( cur != 1 )
        {
            dp[cur] = (( (dp[fa]*cnt[cur])%MOD )*GetInverse(N-cnt[cur]))%MOD;
            ans = (ans + dp[cur]*dp[cur]%MOD)%MOD;
        }
        for ( int i = head[cur]; i != -1; i = D[i].next )
        {
            if ( D[i].v == fa ) continue;
            DFS2( D[i].v, cur );
        }
        return;
    }
    
    int main()
    {
        init();
        int T;
        scanf( "%d", &T );
        while ( T-- )
        {
            scanf( "%d", &N );
            EdgeN = 0;
            memset( head, -1, sizeof(int)*(N+4) );
            for ( int i = 1; i < N; ++i )
            {
                int u, v;
                scanf( "%d%d", &u, &v );
                AddEdge( u, v );
                AddEdge( v, u );
            }
    
            DFS1( 1, -1 );
            ans = dp[1] * dp[1] % MOD;
            DFS2( 1, -1 );
    
            printf("%I64d
    ", ( ans + MOD ) % MOD );
        }
        return 0;
    }
  • 相关阅读:
    活学活用wxPython基础框架
    xpython在Windos下的安装及简单的文本打开、保存
    paramiko实现上传目录
    Python3结合paramiko执行命令
    openstry lua redis实现负载均衡
    定时重启tomcat
    简单的生成随机验证码
    第一个Python小项目:图片转换成字符图片
    Python小练习(二)
    Python小练习(一)
  • 原文地址:https://www.cnblogs.com/GBRgbr/p/3312866.html
Copyright © 2011-2022 走看看