description

给一棵树,问你直径长度与被所有直径经过的边的数量。

solution

直径的长度以及直径的路径可以由两遍dfs求得与记录。
易发现,被所有直径经过的边集可以构成连续一段的路径。
反证:如下图:

因此按上面所说找到任意一条直径路径后,找满足答案路径(边集)的左右断点。
设直径(\(st\)\(ed\)的路径)上的点\(i\)不经过其它直径上的点可以延伸的最长距离为\(d_i\)
如果\(d_i\)等于了\(i\)\(ed\)的距离,说明\(i\)\(ed\)的边都可被替换,因此从左往右找,找到第一个不满足的点即是右端点(右边的全不行)。
同理,从右端点往左找,找到最后一个\(d_i\)不等于\(st\)\(i\)距离的即是左端点。

code

点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+5;
ll dis[N],dist;
int nxt[N],to[N],head[N],len[N],ecnt;
void add_edge(int u,int v,int w) {nxt[++ecnt]=head[u];len[ecnt]=w;to[ecnt]=v;head[u]=ecnt;}
int id,pre[N],com[N];
bool mark[N];
void dfs1(int u,int fa,ll D) {
	if(D>dist) {dist=D;id=u;}
	for(int i=head[u];i;i=nxt[i]) {
		int v=to[i];if(v==fa||mark[v])continue;
		dfs1(v,u,D+len[i]);
	}
}
void dfs2(int u,int fa) {
	if(dis[u]>dist) {dist=dis[u];id=u;}
	for(int i=head[u];i;i=nxt[i]) {
		int v=to[i];if(v==fa)continue;
		dis[v]=dis[u]+len[i];pre[v]=u;
		dfs2(v,u);
	}
}
int main() {
	int n,st,ed;scanf("%d",&n);
	for(int i=1;i<n;i++) {
		int u,v,w;scanf("%d%d%d",&u,&v,&w);
		add_edge(u,v,w),add_edge(v,u,w);
	}
	id=-1;dist=0;dfs1(1,0,0);st=id;
	id=-1;dist=0;dfs2(st,0);ed=id;
	printf("%lld\n",dist);
	for(int i=ed;i;i=pre[i]) {
		mark[i]=1;
//		printf("%d ",i);
		com[pre[i]]=i;
	}
//	puts("");
	int i;
	for(i=st;i;i=com[i]) {
		id=-1;dist=0;dfs1(i,0,0);
		if(dist==dis[ed]-dis[i]) break;
	}
	int ans=0;
	for(;i;i=pre[i]) {
		id=-1;dist=0;dfs1(i,0,0);
		if(dist==dis[i]) break;
		ans++;
	}
	printf("%d",ans);
	return 0;
}