• HDU 4661 Message Passing 【Tree】


    题意:

    给一棵树,每一个结点都有一个信息,每一个时刻,某一对相邻的结点之间可以传递信息,那么存在一个最少的时间,使得所有的节点都可以拥有所有的信息。但是,题目不是求最短时间,而是求最短时间的情况下,有多少种传递方式:某一时刻传递信息的双方不一样则认为是不同的传递方式。(表述的不是很清楚,自己看原题了)


    容易的出,最短的时间内,当然是每个节点将自己的信息想外传出去一次,并且接受一次信息,也就是树边的2倍【2*(n-1)】。

    然后可以证明,在最短时间内,所有的传递方式都有一个“信息转换点”——其他节点的信息首先传递到此节点,然后信息再从这个节点向其他节点传递。

    那么总方案数的计算就是可以枚举每个节点,将这个节点作为根节点,然后求当前情况下的方案——先求以当前节点为根的拓扑排序数,然后平方就是当前方案数。


    拓扑排序数的计算方法:

    c[i] 表示以i为根的子树的节结点(包含结点 i)

    f[i] 表示以i为根的子树的拓扑排序数

    则:当前结点now的拓扑排序数:

    f[now] = f[son1]*f[son2]....f[sonx] * (c[now] - 1)! / (c[son1]! * c[son2]! * ... c[sonx]!)

    即:所有子结点的排列数除以每颗子树所有结点的排列数(去重,对于子树的每一种拓扑序),然后乘以所有子树的拓扑序总数。


    一遍dfs之后,就可以求出根节点的f值,然后在一边dfs,就可以依次求出所有节点的f值。

    转移方法:

    根据父节点的f值计算出去掉当前now节点之后的f值,然后以now为根节点,重新计算即可。看代码实现了。


    这里用到了逆元,整个程序的速度有点慢。求逆元可以用扩展欧几里得算法,这里用的是费马定理,快速幂:x^(P-2)%P 就是x的逆元。


    还有就是树dp,要避免用递归,但是还是用了,会爆栈的,所以就用手工栈了。

    手工栈的方法:http://blog.csdn.net/yang_7_46/article/details/9853061


    //#pragma comment(linker, "/STACK:102400000,102400000")
    
    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    #include <iostream>
    #include <vector>
    using namespace std;
    typedef long long ll;
    #define N 1000002
    #define P 1000000007ll
    
    ll fac[N], f[N], ff[N], cc[N], ans;
    vector<int> g[N];
    int n, c[N];
    
    ll PowerMod(ll a, ll b, ll k) {
        ll tmp = a, ret = 1;
        while (b) {
            if (b & 1) ret = ret * tmp % k;
            tmp = tmp * tmp % k;
            b >>= 1;
        }
        return ret;
    }
    
    void dfs1(int now, int fa) {
        int u;
        c[now]++;
        for (int i=0; i<g[now].size(); i++)
            if ((u = g[now][i]) != fa) {
                dfs1(u, now);
                c[now] += c[u];
            }
    
        ff[now] = cc[now] = 1;
        for (int i=0; i<g[now].size(); i++)
            if ((u=g[now][i]) != fa) {
                ff[now] = ff[now]*f[u] % P;
                cc[now] = cc[now]*fac[c[u]] % P;
            }
        f[now] = (ff[now]*fac[c[now]-1]%P) * PowerMod(cc[now], P-2, P) % P;
    }
    
    void dfs2(int now, int fa) {
        int u;  ll t;
        if (now != 1) {
            t = f[now]*(n-c[now])%P*cc[now]%P;
            t = PowerMod(t, P-2, P);
            f[now] = f[fa]*ff[now]%P*fac[c[now]]%P*t%P;
            ans = (ans + f[now]*f[now]) % P;
        }
        for (int i=0; i<g[now].size(); i++)
            if ((u = g[now][i]) != fa) dfs2(u, now);
    }
    
    int main() {
        int size = 256 << 20; // 256MB
        char *p = (char*)malloc(size) + size;
        __asm__("movl %0, %%esp
    " :: "r"(p));
    
    
        fac[0] = 1; for (int i=1; i<N; i++) fac[i] = fac[i-1]*i % P;
    
        int T; scanf("%d", &T);
        while (T--) {
            scanf("%d", &n);
            for (int i=0; i<=n; i++) { c[i] = 0; g[i].clear(); }
            for (int i=1, x, y; i<n; i++) {
                scanf("%d%d", &x, &y);
                g[x].push_back(y); g[y].push_back(x);
            }
            dfs1(1, 0);
            ans = f[1] * f[1] % P;
            dfs2(1, 0);
    
            printf("%I64d
    ", ans);
        }
    
        return 0;
    }
    








  • 相关阅读:
    关于冥想
    Read Later
    你追求的跟我相反
    UML for Java Programmers之dx实战
    20140525
    面试基础-语言基础篇
    面试基础-linux操作系统篇
    面试基础-数据库篇
    面试基础-计算机网络篇
    Eclipse同时编译多个cpp文件
  • 原文地址:https://www.cnblogs.com/riskyer/p/3249451.html
Copyright © 2020-2023  润新知