hdu6446 网络赛 Tree and Permutation(树形dp求任意两点距离之和)题解

Posted kirinsb

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了hdu6446 网络赛 Tree and Permutation(树形dp求任意两点距离之和)题解相关的知识,希望对你有一定的参考价值。

题意:有一棵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
", 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
", ans);
    }
    return 0;
}

 

以上是关于hdu6446 网络赛 Tree and Permutation(树形dp求任意两点距离之和)题解的主要内容,如果未能解决你的问题,请参考以下文章

Tree and Permutation dfs hdu 6446

HDU-6446 Tree and Permutation

HDU6446 Tree and Permutation

HDU-6446Tree and Permutation

(1009) HDU 6446 Tree and Permutation(规律+树上各个点的距离和)

HDU 6438 Buy and Resell (CCPC网络赛) 堆 贪心