树上结点对路径和

Tree and Permutation

题意:一棵树有n < 1e5个点,每条边有个权值,n个点全排列,若每个排列为a1,a2…..an,
则每个排列的值为(a1,a2),(a2,a3)…..(an-1,an)的和,括号表示两点路径值,求所有排列值的和。


`










`
思路:公式不难推出为2*(n-1)!Dis。Dis为所有结点对的路径和。
直接求Dis,因为有1e5
1e5对,不可。我们转而考虑求每条边的贡献。
每条边连接两个连通块,设两个连通块点数分别为a,b。那么a * b 即为此边的贡献次数。
遍历每条边即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
/*** 
* @Practice Win
* 洛谷 P3379 【模板】最近公共祖先(LCA)
*/
#include<cstdio>
#include<iostream>
#include<cstring>
#pragma comment(linker, "/STACK:102400000,102400000")//解决递归函数多次调用栈溢出问题
using namespace std;
#define Debug(x) cout<<#x<<':'<<x<<endl
#define INF 0x7fffffff//10^9级别,不到2^32
typedef long long ll;
const ll maxn=2e5+11;
const ll mod=1e9+7;
struct Edge{
ll to,w,nx;
}e[maxn];
ll head[maxn],f[maxn];
ll tmp,cnt,n,ans;

void init(){
memset(head,0,sizeof(head));
tmp=2;cnt=1;ans=0;
}

void add(ll u,ll v,ll w){
e[tmp]={v,w};e[tmp].nx=head[u];head[u]=tmp++;
}
ll dfs(ll u,ll fu){
ll sum=1;
for(ll i=head[u];i;i=e[i].nx){
ll v=e[i].to,w=e[i].w;
if(v!=fu){
ll a=dfs(v,u);
sum=sum+a;
ans=(ans+(a*(n-a)%mod)*w%mod)%mod;
}
}
return sum;
}
void solve(){
dfs(1,0);
//cout<<ans<<endl;
ans=(ans*f[n-1])*2%mod;
printf("%lld\n",ans);
}

int main(){
// cin
// cout
f[0]=1;
for(ll i=1;i<maxn;i++){
f[i]=f[i-1]*i%mod;
}
while(scanf("%lld",&n)==1){
init();
for(ll i=1;i<=n-1;i++){
ll u,v,w;
scanf("%lld%lld%lld",&u,&v,&w);
add(u,v,w);add(v,u,w);
}
solve();
//printf("%lld\n",tmp);
}
return 0;
}