Tree and Permutation - hdu6446 - 前向星+找规律+dfs

17 篇文章 0 订阅

题目来源:http://acm.hdu.edu.cn/showproblem.php?pid=6446

思路:

我们很容易能发现,全排列有n!种,其中任意两点i到j会走(n-1)!次,那么我们以任意边为例,当该边一侧有x个点,那另一侧有n-x个点,那么把边左右两点两两结成一对,就有(n-x)*x对,每一对点走(n-1)!次,那么对于一条边走过的次数,就有sum=2*(n-x)*x*(n-1)!

乘2是因为从i到j也可从j到i

(这里不要迷糊啊,点对和边不是一样的,对于样例1,点对可以是:1,2;1,3;2,3…;而边只能是1,2;2,3)

代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring> 
#include<queue>

#define ll long long
using namespace std;

const int N=100005,mod=1e9+7;
const ll INF=0x3f3f3f3f3f3f3f3f;
struct proc{int to,cost,next;};
proc edge[2*N];
int n,tot,head[N];
ll d[N],f[N],ans=0;
 
void init(){
	memset(head,-1,sizeof(head));
	memset(d,0,sizeof(d));
	tot=0;
}
 
void addEdge(int from,int to,int cost){
	edge[tot].to=to;
	edge[tot].cost=cost;
	edge[tot].next=head[from];
	head[from]=tot;
	tot++;
}
 
int dfs(int root,int fa){
	d[root]=1;
	for(int i=head[root];i!=-1;i=edge[i].next){//遍历以root为起点的边 
		int a=edge[i].to,b=edge[i].cost;
		if(a==fa)continue;
		dfs(a,root);
		d[root]+=d[a];
		ans=(ans+(n-d[a])%mod*d[a]%mod*b%mod)%mod;
	}

}
 
int main(){
	int s,e,c;
	f[0]=1;
	for(int i=1;i<=N;i++)f[i]=(f[i-1]*i)%mod;
	while(scanf("%d",&n)!=EOF){
		init();
		for(int i=1;i<n;i++){
			scanf("%d%d%d",&s,&e,&c);
		    addEdge(s,e,c);
		    addEdge(e,s,c);
		}
		ans=0;
		dfs(1,-1);
		printf("%lld\n",(2*ans%mod*f[n-1])%mod);
	}
}

当时看这个题时,我也发现了答案里:任意两点出现的次数是(n-1)!

(eg点1,2,3。12;13;23;21;31;32,这些点对出现2次)

但是我不知道怎么求长度,因为比如样例1:我想是把每个点对的长度求一下,12和23连着,若求13,我不知道怎么求。想了好久……

然后看了题解,我内心:???还能这样,果然还是too young, too naive

还是写题太少啦,不过现在知道了,点对出现的次数是相同的,那么我们转移到边上去求。

顺便又看了眼前向星:https://blog.csdn.net/Binary_Heap/article/details/78209086

(ACM之路,道阻且跻,我这种菜鸡做到尽人事听天命好啦,不要给自己太大压力,没什么的,心态要好!每次历练都是十分难得的~)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值