1. 程式人生 > >hdu6446 網絡賽 Tree and Permutation(樹形dp求任意兩點距離之和)題解

hdu6446 網絡賽 Tree and Permutation(樹形dp求任意兩點距離之和)題解

eof dfs 版本 ini pre nbsp i++ 公式 net

題意:有一棵n個點的樹,點之間用無向邊相連。現把這棵樹對應一個序列,這個序列任意兩點的距離為這兩點在樹上的距離,顯然,這樣的序列有n!個,加入這是第i個序列,那麽這個序列所提供的貢獻值為:第一個點到其他所有點距離之和。求所有序列貢獻值之和。

思路:假如第一個點是k,那麽後面n-1個點共有(n - 1)!種排列,也就是說,第一個點是k那麽這樣的序列的貢獻值為(n - 1)!*(k到其他點距離之和),顯然最後答案應該是所有點之間的距離和的兩倍 *(n - 1)!。問題轉化為了求一棵樹上所有點之間的距離,怎麽求呢?

假設有一條邊E,那麽如果要經過E這條邊,必然是兩個端點在E的兩邊,假設左邊有M點,右邊有(n - M)個點,那麽一共經過E次數2 *(n - M)* M,所以E的貢獻長度為2 *(n - M)* M * E的權值,最後把每條邊的貢獻長度加在一起就是所有點之間的距離。至於求E兩邊點數,只要算每個點的子節點數(包括自己)就行了。

MOD值賦錯了wa了一下午...寫了兩個版本...

標解:

技術分享圖片

參考:HDU2376Average distance(樹形dp|樹上任意兩點距離和的平均值)

代碼:

/*DP*/
#include<map>
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn = 100000 + 10;
const
int seed = 131; const int MOD = 1000000000 + 7; const int INF = 0x3f3f3f3f; struct Edge{ ll w; int u, v, next; }edge[maxn << 1]; ll fac[maxn], dp[maxn], num[maxn], ans; int head[maxn], tot, n; void init(){ fac[0] = 1; for(int i = 1; i < maxn; i++){ fac[i] = (fac[i - 1] * i) % MOD; } }
void addEdge(ll u, ll v, ll w){ edge[tot].u = u; edge[tot].v = v; edge[tot].w = w; edge[tot].next = head[u]; head[u] = tot++; } void dfs(int u, int pre){ num[u] = 1; for(int i = head[u]; i != -1; i = edge[i].next){ int v = edge[i].v; ll w = edge[i].w; if(v == pre) continue; dfs(v, u); num[u] += num[v]; dp[u] = (dp[u] + dp[v] + w * num[v] % MOD * (n - num[v]) % MOD) % MOD; } } int main(){ init(); while(~scanf("%d", &n)){ memset(head, -1, sizeof(head)); memset(dp, 0, sizeof(dp)); tot = 0; for(int i = 0; i < n - 1; i++){ ll u, v, w; scanf("%lld%lld%lld", &u, &v, &w); addEdge(u, v, w); addEdge(v, u, w); } ans = 0; dfs(1, -1); ans = dp[1] * 2LL % MOD * fac[n - 1] % MOD; printf("%lld\n", ans); } return 0; }
/*直接代公式*/
#include<map>
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn = 100000 + 10;
const int seed = 131;
const int MOD = 1000000000 + 7;
const int INF = 0x3f3f3f3f;
struct Edge{
    ll w;
    int u, v, next;
}edge[maxn << 1];
ll fac[maxn], ans;
int head[maxn], family[maxn], tot, n;
void init(){
    fac[0] = 1;
    for(int i = 1; i < maxn; i++){
        fac[i] = (fac[i - 1] * i) % MOD;
    }
}
void addEdge(ll u, ll v, ll w){
    edge[tot].u = u;
    edge[tot].v = v;
    edge[tot].w = w;
    edge[tot].next = head[u];
    head[u] = tot++;
}
int dfs(int u, int pre){
    family[u] = 1;
    for(int i = head[u]; i != -1; i = edge[i].next){
        if(edge[i].v == pre) continue;
        family[u] += dfs(edge[i].v, u);
    }
    return family[u];
}
int main(){
    init();
    while(~scanf("%d", &n)){
        memset(head, -1, sizeof(head));
        tot = 0;
        for(int i = 0; i < n - 1; i++){
            ll u, v, w;
            scanf("%lld%lld%lld", &u, &v, &w);
            addEdge(u, v, w);
            addEdge(v, u, w);
        }
        ans = 0;
        dfs(1, -1);
        for(int i = 0; i < tot; i += 2){
            ll temp;
            temp = (2LL * family[edge[i].v] * (n - family[edge[i].v])) % MOD;
            temp = (temp * edge[i].w) % MOD;
            temp = (temp * fac[n - 1]) % MOD;
            ans += temp;
            ans %= MOD;
        }
        printf("%lld\n", ans);
    }
    return 0;
}

hdu6446 網絡賽 Tree and Permutation(樹形dp求任意兩點距離之和)題解