HDU - 6446 Tree and Permutation (树形dp +找规律)

http://acm.hdu.edu.cn/showproblem.php?pid=6446

题意:

对于1个序列,全排列,每次都从头遍历到尾,问能有这N!的距离之和为多少

思路:

首先只是一颗无根树,那么随便找一个根节点,可以求出每个点到其他任意点的距离和,
假设有这么一棵树,假设根节点为1

这里写图片描述

void DFS(int u, int parent) {
    num[u] = 1;
    for (int i = head[u]; i + 1; i = edge[i].nxt){
        int son = edge[i].v;
        if(son == parent) continue;
        DFS(son, u);
        num[u] += num[son];
        sum = (sum + (num[son] * ((n - num[son]) * edge[i].w) % M) % M) % M;
    }
}

而对于这个

 sum = (sum + (num[son] * ((n - num[son]) * edge[i].w) % M) % M) % M;

可以这样解释,对于2的子节点3来说,3的子树的每一个点都要经过2->3这条边(n-num[3])次,那所有的就要经过(num[3](n-num[3]))次了,而权值为w,那么2->3这条边一共贡献了(num[3](n-num[3]) × w)这么多

在后来就可以通过暴力打表可以得出n的点时每条边出现的次数是相同的为一定值,两者相乘即可

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 1e5 + 5;
const int M = 1e9 + 7;

struct Edge{
    int v, w, nxt;
}edge[2 * maxn];
int head[maxn], tot, n;
ll sum, num[maxn], bb[maxn];

void init() {
    tot = 0;
    memset(head, -1, sizeof(head));
    sum = 0;
}

void db() {
    bb[2] = 2;
    for (int i = 3; i < maxn; i ++)
        bb[i] = (bb[i - 1] * (i - 1)) % M;
}


void addEdge(int u, int v, int w) {
    edge[tot].v = v;
    edge[tot].w = w;
    edge[tot].nxt = head[u];
    head[u] = tot ++;
}

void DFS(int u, int parent) {
    num[u] = 1;
    for (int i = head[u]; i + 1; i = edge[i].nxt){
        int son = edge[i].v;
        if(son == parent) continue;
        DFS(son, u);
        num[u] += num[son];
        sum = (sum + (num[son] * ((n - num[son]) * edge[i].w) % M) % M) % M;
    }
}


int main() {
    db();
    while(scanf("%d", &n) != EOF) {
        init();
        for (int i = 1; i < n; i ++) {
            int u, v, w;
            scanf("%d %d %d", &u, &v, &w);
            addEdge(u, v, w);
            addEdge(v, u, w);
        }
        DFS(1, 0);
        printf("%d\n", (sum * bb[n]) % M);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/henu_jizhideqingwa/article/details/82110050
今日推荐