补题向 | ccpc网络预选赛—I Tree and Permutation

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

Input

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

Output

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

Sample Input

3
1 2 1
2 3 1
3
1 2 1
1 3 2

Sample Output

16
24

n个点,给出n-1条线(不会构成环)及其权值,求以这n个点的全排列为路径的权值之和

假设xy=a,而在n个点的全排列中,x点和y点相邻的情况出现的次数是2*(n-1)*(n-2)!

(2:xy=yx;n-1:xy在n个点中有n-1种位置可放;(n-2)!:放好xy之后,剩下的n-2个数任意放,即A n-2 取 n-2)

所以问题变为所有点对的距离*权值*次数之和

因为所有点对次数是一样的,所以关键在于求出所有点对的距离*权值之和

队友超级机智,相处了一个好办法,假设题目给了xy的长度,因为不可能出现环,所以路径是唯一的

ac的距离必定包含xy,也就是说可以算出经过每一段的次数*权值

对于线段xy,左边有a、x、b三个点,右边有y、c两个点,所以经过线段xy的次数肯定是2*3=6次,而且若已知一边的点数w,另一边的点数也是知道的(n-w)

如果把图建成一棵树,对于每一条枝干(线段),线段指向的父节点及其子节点总和就相当于线段一边的所有点数

#include<stdio.h>
#include<vector>
#include<algorithm>
#include<string.h>
#include<iostream>
#include<fstream>
#include<math.h>
#include<stack>
#include<queue>
#include<bitset>
#include<utility>
#include<set>
#include<map>
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
typedef long long ll;
const double eps=0.0000000000001;
const ll mod=1000000007;
int t;
int n;
struct P{
    int x;
    int y;
    ll v;
    int nt;
};
P e[100005];
int head[100005];
int w[100005];
bool f[100005];

ll jc(int n){
	//计算阶乘 
    ll ans=1;
    while(n>=1){
        ans*=n;
        ans%=mod;
        n--;
    }
    return ans;
}
ll gt(int s){
	//深搜遍历整棵树,得到每个顶点子节点+1(自己)的数量 

	if(head[s]==-1){
		w[s]=1;
		return w[s];	
	}
	ll ww=0;	
    for(int i=head[s];i!=-1;i=e[i].nt){

    		ww+=gt(e[i].y);   		
  	
    }
	ww++;
	w[s]=ww;
	return ww; 
}
void add_e(int i,int x,int y,ll v){
	
	e[i].x=x;
	e[i].y=y;
	e[i].v=v;
	e[i].nt=head[x];
	head[x]=i;
}
int main(){
    while(~scanf("%d",&n)){
        memset(head,-1,sizeof(head));
        memset(f,0,sizeof(f));
        int x,y;
		ll v;
        for(int i=0;i<n-1;i++){
            scanf("%d%d%Illd",&x,&y,&v);
            if(f[y]==0){
            	add_e(i,x,y,v);
            	f[y]=1;
            }
            else{
            	add_e(i,y,x,v);
            	f[x]=1;
            }
            //使图成为一棵树,如果已经有点指向y点了,就反向插入边 
        }
        memset(w,0,sizeof(w));
        
        for(int i=1;i<=n;i++){
        	if(f[i]==0){
        		gt(i);
        		break; 
        	}//找到根节点求出w 
        }
        /*
       	for(int i=1;i<=n;i++){
       		cout<<i<<" "<<w[i]<<endl;
       	} 
       	*/
        ll ans=0;
        for(int i=0;i<n-1;i++){
            ll sum=0;
            sum=(ll)w[e[i].y]*(n-w[e[i].y]);
            sum%=mod;
            //经过边i的次数 
            sum*=e[i].v;sum%=mod;
            //边i对答案贡献的价值量 
            ans+=sum;ans%=mod;
			//所有边累加                 
        }
        ans*=jc(n-2);  ans%=mod;       
        ans*=2;        ans%=mod;
        ans*=(n-1);    ans%=mod; 
        printf("%lld\n",ans);
    }
    return 0;
}

这道题没做出来真的好不甘心啊!o(╥﹏╥)o

已经把基本思路都想出来了,可是没有考虑到建图的时候要建成树,想出思路的时候很兴奋直接就开始打,没有先把所有环节理清楚,想着边打边顺,但是图论的题真的会越改越懵,还是改不了急躁,感觉坑了一波队友 (>人<)

感觉现在已经是什么时候放弃都不奇怪的状态了,但是至少挣扎到博客文章数破80  (ノへ ̄、)

猜你喜欢

转载自blog.csdn.net/bekote/article/details/82085955
今日推荐