2018 CCPC网络赛1009 Tree and Permutation (HDU6446)(dfs or 树形dp)

本文围绕2018年CCPC网络赛中的Tree and Permutation问题展开。给出树的节点和边信息,按节点全排列求根节点到其他点最短路径之和的总和。介绍了作者队伍比赛时AC的思路,通过两遍dfs求解,第一遍存子树答案和节点数,第二遍求最终值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Tree and Permutation
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 169 Accepted Submission(s): 57

Problem Description
There are N vertices connected by N−1 edges, each edge has its own length.
The set { 1,2,3,…,N } contains a total of N! unique permutations, let’s say the i-th permutation is Pi and Pi,j is its j-th number.
For the i-th permutation, it can be a traverse sequence of the tree with N vertices, which means we can go from the Pi,1-th vertex to the Pi,2-th vertex by the shortest path, then go to the Pi,3-th vertex ( also by the shortest path ) , and so on. Finally we’ll reach the Pi,N-th vertex, let’s define the total distance of this route as D(Pi) , so please calculate the sum of D(Pi) for all N! permutations.

Input
There are 10 test cases at most.
The first line of each test case contains one integer N ( 1≤N≤105 ) .
For the next N−1 lines, each line contains three integer X, Y and L, which means there is an edge between X-th vertex and Y-th of length L ( 1≤X,Y≤N,1≤L≤109 ) .

Output
For each test case, print the answer module 109+7 in one line.

Sample Input
3
1 2 1
2 3 1
3
1 2 1
1 3 2

Sample Output
16
24

题意
给出一颗树,按节点进行全排列,以全排列的第一个数为根节点,求出根节点到其他点的最短路径之和(说是最短路径,仔细想想(题目说了)就是树的节点之间的距离),然后整个再求一次和
题解
官方题解的方法比较简单,这里是我队比赛时AC的思路,首先队友读题,发现问题可以变成所有点能到达的距离和乘 (n1)! ( n − 1 ) ! (简单的排列问题),队友把求和丢给我,然后我开始了自闭之旅,辜负了队友的希望,还好最后挽回了一点颜面,A了这题。
这里说一下我求和的思路,
第一遍dfs可以求出点1(随便选个点当根)的答案,存下以一个点为根的子树的答案和子树包含的节点数量。
第二遍dfs就可以根据
ans2[v]=ans1[v]+(ans2[u]ans1[v]chi[v]w)+(nchi[v])w a n s 2 [ v ] = a n s 1 [ v ] + ( a n s 2 [ u ] − a n s 1 [ v ] − c h i [ v ] ∗ w ) + ( n − c h i [ v ] ) ∗ w 求出最终的值

#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
typedef vector<int> VI;
typedef long long ll;
typedef pair<int,int> PII;
const ll mod=1000000007;
ll powmod(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
const int maxn = 100010;
vector<pair<int,ll>> e[maxn];
int vis[maxn];


ll ans1[maxn], ans2[maxn], chi[maxn];
ll ans;
int n;
void dfs1(int u)
{
    vis[u] = 1;
    chi[u] = 0;
    for(auto &v:e[u])
    {
        if(!vis[v.first])
        {
            dfs1(v.first);
            chi[u] += chi[v.first];
            ans1[u] +=  ans1[v.first]%mod + chi[v.first]%mod*v.second%mod;
            ans1[u]  %= mod;
        }
    }
    chi[u]++;
}


void dfs2(int u)
{
    if (u == 1)
        ans2[u] = ans1[u];
    vis[u] = 1;
    for(auto &v:e[u])
    {
        if(!vis[v.first])
        {
            ans2[v.first] = (ans1[v.first]%mod + (ans2[u]%mod - ans1[v.first]%mod - chi[v.first]%mod*v.second%mod + mod + mod)%mod + (n - chi[v.first])%mod*v.second%mod + mod)%mod;
            dfs2(v.first);
        }
    }
}

int main()
{

    while(scanf("%d", &n)!=EOF)
    {
    rep(i,1,n+1)
    {
        e[i].clear();
        vis[i] = 0;
        ans1[i] = 0;
        ans2[i] = 0;
    }
    rep(i,1,n)
    {
        int u,v;
        ll w;
        scanf("%d%d%lld",&u,&v,&w);
        e[u].push_back(make_pair(v,w));
        e[v].push_back(make_pair(u,w));
    }
    ans = 0;
    dfs1(1);
    memset(vis, 0, sizeof(vis));
    dfs2(1);
    rep(i,1,n+1)
        {
            ans += ans2[i];
            ans %= mod;
        }   
    if(n == 1)
        printf("0\n");
    else if(n == 2)
    {
        printf("%lld\n",ans%mod);
    }   
    else
    {
        ll t = 1;
        rep(i,1,n)
        {
            t *= i;
            t %= mod;
        }
        ans = ans%mod*t%mod;
        printf("%lld\n", ans %mod);
    }  
    }
    return 0;
}

最后,这真是一场打的十分难受的网络赛!!!
如何评价2018年ccpc网络赛?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值