【2018/07/26测试T3】【SOJ 1912】连通能力

【题目】

题目描述:

Star善于胡思乱想,这些天他又为带权树定义了一个叫“连通能力”的奇怪属性。对于一棵边上有权值的树( N 个结点 N-1 条边的无向连通图),我们按以下方法定义其连通能力:

1、规定某结点的代价为它到其它结点的距离(简单路径所经过边的权值和)的最大值; 
2、代价最小的结点的代价作为这棵树的连通能力。

设某棵给定的树以 1 号结点为根,Star 想考察以任意结点为根的子树的连通能力有多大。请你帮助他把这 N 个值快速求出来。

输入格式:

第一行一个整数N;
接下来 N-1 行,每行三个整数 u、v、w 表示结点 u、v 间存在权值为 w 的边。

输出格式:

输出 N 行 N 个整数,第 i 行的值表示以结点 i 为根的子树所对应的连通能力。

样例数据:

输入
7
2 1 1
3 1 2
4 2 3
5 2 4
6 3 5
7 3 6

输出
7
4
6
0
0
0
0

备注:

【数据范围】 
对于 20% 的数据,1 ≤ N ≤ 300,所有边均与 1 号点相连;
对于 40% 的数据,1 ≤ N ≤ 300;
对于 60% 的数据,1 ≤ N ≤ 4000;
对于 80% 的数据,1 ≤ N ≤ 100000;
对于 100% 的数据,1 ≤ N ≤ 1000000、1 ≤ w ≤ 10000。

 

【分析】

首先,前20分是非常好得的,以第1个点为根的子树的连通能力是最大的边,以其他点为根的子树的连通能力是0

果断地写了20分的代码,然后……就没有然后了

来说一下正解吧,我们常把树的直径定义为树中最远的两个点的距离,从这个角度来说,我们把我们要求的东西定义为“树的半径”,那么,有一个结论,树的半径在树的直径上,证明如下:

那么,树的半径其中一个端点就是直径的一个端点,另一个就是离直径中点最近的一个点(这个大家感性理解一下就行了)

求树的直径参考树的直径,这里我们用树形DP来做

中点的话我们考虑用倍增来求,这样的话时间复杂度为O(N * log N)听说还有O(N)的算法但本蒟蒻不会

还有一些具体的细节就看代码吧

下面对代码中的数组进行解释:

  • first,v,w,next 是用前向星存储领接表
  • father 是倍增操作
  • d[ i ] 表示以 i 为根的子树的直径
  • r[ i ] 表示以 i 为根的子树的半径
  • f1[ i ],f2[ i ] 分别表示以 i 为根的子树的最长链和次长链
  • dep1[ i ],dep2[ i ] 分别表示以 i 为根的子树中最长链和次长链所到达的叶子节点
  • dis[ i ] 表示 i 到根节点(也就是 1)的距离

 

【代码】

另外,不用 inline 的话会有一组数据 RE,本蒟蒻也不知道为什么,路过的大佬可以解释一下吗?

还有话说为什么 cout 比 printf 快啊?

附上 AC 代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define L 25
#define N 1000005
#define M 2000005
using namespace std;
int t,first[N],v[M],w[M],next[M],father[N][L];
int d[N],r[N],f1[N],f2[N],dis[N],dep1[N],dep2[N];
void add(int x,int y,int z)
{
	t++;
	next[t]=first[x];
	first[x]=t;
	v[t]=y;
	w[t]=z;
}
inline void getfather(int x)
{
	int i,j;
	for(i=first[x];i;i=next[i])
	{
		j=v[i];
		if(j==father[x][0])
		  continue;
		father[j][0]=x;
		getfather(j);
	}
}
inline void search(int x)
{
	int i,j,depth;
	dep1[x]=x;
	dep2[x]=x;
	for(i=first[x];i;i=next[i])
	{
		j=v[i];
		if(j==father[x][0])
		  continue;
		dis[j]=dis[x]+w[i];
		search(j);
		if(r[j]>r[x])
		{
			d[x]=d[j];
			r[x]=r[j];
		}
		if(f1[x]<f1[j]+w[i])
		{
			f2[x]=f1[x];
			dep2[x]=dep1[x];
			f1[x]=f1[j]+w[i];
			dep1[x]=dep1[j];
		}
		else if(f2[x]<f1[j]+w[i])
		{
			f2[x]=f1[j]+w[i];
			dep2[x]=dep1[j];
		}
	}
	depth=dep1[x];
	if(d[x]<f1[x]+f2[x])
	{
		d[x]=f1[x]+f2[x];
		for(i=20;i>=0;--i)
		  if(depth!=x&&(f2[x]+dis[father[depth][i]]-dis[x]>=f1[x]-dis[father[depth][i]]+dis[x]))
		    depth=father[depth][i];
		r[x]=f2[x]+dis[depth]-dis[x];
		if(depth!=x&&f1[x]-dis[father[depth][0]]+dis[x]<r[x])
		  r[x]=f1[x]-dis[father[depth][0]]+dis[x];
	}
}
int main()
{
//	freopen("connect.in","r",stdin);
//	freopen("connect.out","w",stdout);
	int n,i,j,x,y,z;
	scanf("%d",&n);
	for(i=1;i<n;++i)
	{
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z);
		add(y,x,z);
	}
	getfather(1);
	for(j=1;j<=20;++j)
	  for(i=1;i<=n;++i)
	    father[i][j]=father[father[i][j-1]][j-1];
	search(1);
	for(i=1;i<=n;++i)
	  cout<<r[i]<<'\n';
//	fclose(stdin);
//	fclose(stdout);
	return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值