Luogu_P3304 [SDOI2013]直径 树的直径

Luogu_P3304 [SDOI2013]直径

树的直径


题目链接
问的是树的几条直径中有几条边相交
设直径上一点到左端点的距离为\(lf[i]\)
到右端点的距离为\(rt[i]\)
到不过直径上点的最长链长度为\(fr[i]\)
明显就是找到两个端点
\(lf[x]=fr[x]\)\(rt[y]=fr[y]\)
求出两端点之间的边的个数就可以


代码如下:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=200010;
int n,ls,fs,head[maxn],tot,v[maxn],d[maxn],fr[maxn],fa[maxn],a[maxn],cnt;
int lf[maxn],rt[maxn];
struct node{
	int nxt,to,dis;
	#define nxt(x) e[x].nxt
	#define to(x) e[x].to
	#define dis(x) e[x].dis
}e[maxn<<1];
inline void add(int from,int to,int dis){
	to(++tot)=to;dis(tot)=dis;nxt(tot)=head[from];head[from]=tot;
}
void dfs(int x){
	v[x]=1;
	for(int i=head[x];i;i=nxt(i)){
		int y=to(i);
		if(v[y]) continue;
		fa[y]=x;
		d[y]=d[x]+dis(i);
		fs=(d[y]>=d[fs]) ? y : fs;
		dfs(y);
	}
	v[x]=0;
}
inline void geta(){
	while(fs!=ls){
		v[fs]=1;a[++cnt]=fs;
		fs=fa[fs];
	}
	a[++cnt]=ls;v[ls]=1;
}
inline void getfr(int x){
	v[x]=1;
	for(int i=head[x];i;i=nxt(i)){
		int y=to(i);
		if(v[y]) continue;
		getfr(y);
		fr[x]=max(fr[x],fr[y]+dis(i));
	}
}
signed main()
{
	scanf("%lld",&n);
	for(int x,y,z,i=1;i<n;i++){
		scanf("%lld%lld%lld",&x,&y,&z);add(x,y,z);add(y,x,z);
	}
	dfs(1);
	memset(d,0,sizeof(d));ls=fs;fs=0;
	dfs(ls);
	printf("%lld\n",d[fs]);int ds=d[fs];
	geta();
	for(int i=1;i<=cnt;i++){
		getfr(a[i]);
		lf[a[i]]=ds-d[a[i]];rt[a[i]]=d[a[i]];
		// cout<<a[i]<<" "<<fr[a[i]]<<" "<<lf[a[i]]<<" "<<rt[a[i]]<<endl;
	}
	int ll=0,rr=cnt;
	for(int i=1;i<=cnt;i++){
		if(fr[a[i]]==lf[a[i]]) ll=i;
		if(fr[a[i]]==rt[a[i]]){
			rr=i;break;
		}
	}
	// cout<<rr<<" "<<ll<<endl;
	printf("%lld\n",max(1ll*0,abs(rr-ll)));
	return 0;
}
posted @ 2019-10-14 09:11  ChrisKKK  阅读(136)  评论(0编辑  收藏  举报