【树上点对距离和】HDU - 6446 - 2018CCPC - 网络选拔赛 - Tree and Permutation

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/monochrome00/article/details/82291068

题目链接<http://acm.hdu.edu.cn/showproblem.php?pid=6446>


题意:

给出一个n个节点的树。对于1~n的全排列计算价值和,每一个排列的价值为按排列顺序走的权值和。


题解:

很容易得知每一种点对距离出现的次数为2*(n-1)!,只要算出所有的点对距离和再乘上2*(n-1)!就是答案。

我们考虑点对距离和中每条边出现的次数。

如果把边<u,v>断开,可以将树分为两部分,以u为根节点的树和以v为根节点的树。两个树节点个数的乘积就是这条边出现的次数。

而一条边两边的节点个数用一次搜索就可以得出。


#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const ll N=1e5+7;
ll fac[N];
void init(){
    fac[0]=1;
    for(ll i=1;i<N;i++) fac[i]=fac[i-1]*i%mod;
}
struct Edge{
    ll u,v,w,nxt;
    Edge(ll u=0,ll v=0,ll w=0,ll nxt=0):u(u),v(v),w(w),nxt(nxt){}
}e[2*N];
ll p[N],edn;
ll ans=0;
void add(ll u,ll v,ll w){
    e[++edn]=Edge(u,v,w,p[u]);p[u]=edn;
    e[++edn]=Edge(v,u,w,p[v]);p[v]=edn;
}
bool vis[N];
ll tot,n;
void dfs(ll u,ll nu){
    ll num=nu;
    for(ll i=p[u];~i;i=e[i].nxt){
        ll v=e[i].v;
        if(vis[v]) continue;
        vis[v]=true;
        tot++;
        dfs(v,tot);
        ans=(ans+e[i].w*((n-(tot-num))*(tot-num)%mod)%mod)%mod;
        num=tot;
    }
}
int main()
{
    ll u,v,w;
    init();
    while(scanf("%lld",&n)!=EOF){
        memset(p,-1,sizeof(p));edn=-1;
        memset(vis,false,sizeof(vis));
        for(ll i=1;i<n;i++){
            scanf("%lld%lld%lld",&u,&v,&w);
            add(u,v,w);
        }
        ans=0;tot=1;vis[1]=true;
        dfs(1,1);
        ll tmp=fac[n-1]*2%mod;
        printf("%lld\n",ans*tmp%mod);
    }
}

猜你喜欢

转载自blog.csdn.net/monochrome00/article/details/82291068