HDU - 6446 Tree and Permutation 树状dp + 思维分析( 2018ccpc网络赛1009)

一.在看这个题之前,我们先看一下  HDU - 2376 Average distance 这个题:

1.题意:给你n个节点的一棵树,让你求树上所有路径和的平均距离即

(d12 + d13 + d14 +... + d1n + d23 + d24 + .... + d2n + .....d(n-1)n) / (n*(n-1)/2)

2.分析:求树上的平均距离,如果我们暴力肯定超时,所以我们这里转换一下思维(前方高能):

(1)求的所有距离无非是图上 任意两点间的距离和 ,而所有的距离都可以拆分为单独边的组合,所以图上的总距离和 = 每条独立边的出现次数 * 它的贡献 , 那么问题转化为怎么求每条独立边的出现次数?

 (2)上面我们说了,是求任意两点的距离和,那么对于一条独立边,它必然把树分为两侧,左侧有n个点,右侧有m个点,那么这条边必然要被经过 (n*m)次(因为要求任意两点的距离和,所以两侧的点毕然都要联通那就必须经过这条边),所以每条独立边的和是 (n*m)*val,所以总距离和 = 每条独立边的总贡献之和

(3)那么怎么求这条边的左右两侧的点数呢?这里就用树状dp来保存这个点和他子节点的点数之和sum[a] , 那么n - sum[a]就是这条边另一侧的点数,所以求得这条边的总贡献。然后树状dp保存每条边和他孩子的所有贡献即可。

3.代码:

#include <iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn = 10000 + 7;
struct Edge
{
    int to,next,v;
}edge[2*maxn];
int n,tot,head[maxn];
LL sum[maxn],dp[maxn];
void addEdge(int a,int b,int c){//链式前向星优化
    edge[tot].next = head[a];edge[tot].to = b;edge[tot].v = c;head[a] = tot++;
}
void dfs(int a,int father){
    sum[a] = 1;//改点初始化它和它的子节点共1个
    for(int i = head[a];~i;i = edge[i].next){//循环子节点
        int val = edge[i].v;
        int son = edge[i].to;
        if(son==father)continue;//碰到父节点重新找(父节点非子节点)
        dfs(son,a);//dfs
        sum[a]+=sum[son];//累加这条边(链接a和子节点son的边)下的子节点数
        dp[a] = dp[a] + dp[son] +(LL)((n-sum[son])*sum[son]*val);//累加这条边的总贡献
    }
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        tot = 0;
        memset(head,-1,sizeof(head));
        memset(sum,0,sizeof(sum));
        memset(dp,0,sizeof(dp));
        scanf("%d",&n);
        for(int i = 0;i<n-1;i++){
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            addEdge(a,b,c);
            addEdge(b,a,c);
        }
        dfs(0,0);//从节点0出发
        int temp = n*(n-1)/2;
        printf("%.6lf\n",dp[0]*1.0/temp);
    }
    return 0;
}

二.言归正传(HDU - 6446 Tree and Permutation )

1.题意:给你n个点的一棵树,然后让你求n个节点全排列时,先求每个排列的第一个节点为根的到其他节点最短距离的距离和,再把这些全排列的距离和再求和。(好吧,刚开始读错题了,自闭.....)

2.分析:

(1)比如给你n = 3:则我们需要求

1 2 3 :d12 + d13 = s1

1 3 2:d13 + d12 = s2

2 1 3:d21 + d23 = s3

2 3 1:d23 + d21 = s4

3 1 2:d31 + d32 = s5

3 2 1 :d32 + d31 = s6

sum = s1+s2+s3+s4+s5+s6 , sum即为所求

(2)假如给你一个n的树,我们先分析d12这条边:

因为首先,d12出现在每个以1开头的全排列里面。而对于n个点的以1开头的全排列有(n-1)!个,所以d12先出现了(n-1)!次。

其次,d21 = d12 , 所以在以2开头的全排列里面,同理可得,d21也出现了(n-1)!次

综上:d12共出现了2*(n-1)!次 , 同理可得每条边在全排列里面都出现了 2 *(n-1)!次。

(3)所以 sum = 2*(n-1)! *(d12 +d13 +d14 + ...+d1n + d23 + d24 + ..... + d(n-1)n)

所以我们发现(d12 +d13 +d14 + ...+d1n + d23 + d24 + ..... + d(n-1)n)这不就是树上的距离和嘛!这不是刚刚求的嘛!所以这里用树状dp处理一下,乘上次数就ok拉!

3.代码:

#include <iostream>
#include <string>
#include <cstring>
#include <algorithm>
#include <ostream>
#include <fstream>
#include <cstdio>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <math.h>
#define mod 1000000007
using namespace std;
typedef  long long LL;
const int maxn = 100000 + 7;
LL num[maxn],dp[maxn],sum[maxn];
int tot,head[maxn];
int n;
struct Edge
{
    int to,next;
    LL v;
}edge[4*maxn];
void addEdge(int a,int b,LL c){//链式前向星优化
   edge[tot].next = head[a];edge[tot].to = b;edge[tot].v = c;head[a] = tot++;
}
void dfs(int a,int father){
    sum[a] = 1;
    for(int i = head[a];~i;i = edge[i].next){
        int son = edge[i].to;
        LL val = edge[i].v;
        if(son==father)continue;
        dfs(son,a);
        sum[a]+=sum[son];
        dp[a] = (dp[a] + dp[son] + (sum[son]*(n-sum[son])*val)%mod)%mod;
    }

}
int main()
{
    num[1] = 1;
    for(int i = 2;i<=maxn;i++){//预处理一下(n-1)!
        num[i] = (num[i-1]*i)%mod;
    }
    while(scanf("%d",&n)!=EOF){
        tot = 0;
        memset(head,-1,sizeof(head));
        memset(sum,0,sizeof(sum));
        memset(dp,0,sizeof(dp));
        for(int i = 0;i<n-1;i++){
            int a,b;
            LL c;
            scanf("%d%d%lld",&a,&b,&c);
            addEdge(a,b,c);
            addEdge(b,a,c);
        }
        dfs(1,1);//树状dp求距离和
        printf("%lld\n",((dp[1]*2)%mod*num[n-1])%mod);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40772692/article/details/82078277