很久没有写博客了。。中间有刷题哦!朕没有偷懒!
ps看我博客的改版没有。。【朕也是会写那一点点HTML的人哈哈哈】
这题损耗老朽身心无数。。。【【哀家不要打我】】
题意很清晰喽。
第一问,求树的直径 – 这个可以 两边BFS。
第二问,求所有树的直径必须经过的边。
分析:
观察 & 从 zxy 学长的博客中可以发现,任选一个点之后BFS最远距离的那个点。那个点必然是直径的一个端点。【【也可以用2遍dfs】】
主要是第二问,这道题我们可以 分析【瞎搞】出一个性质 如果直径上的点在不是直径的link上的最大距离到它的直径的一个端点的距离如果相等那它一定不是必经过的点。
意思就是
如果是相等的,那它范围必然可以缩小。
比如:
所以这道题就是可以写的。
辣么为什么我WA了?很多次?
第一,边的范围又一次按点开的。。。
第二,边的权值足以到 long long 没开 :)
第三,其中循环直径上的每个点 循环错了。你敢信?然而我就这么错了。。。
总结完毕。
代码一向比较长。。。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<iostream>
#define LL long long
using namespace std;
//by mars_ch
int n,cnt,ans,tot;
struct Edge
{
int s,t,nxt;
LL v;
}e[400001];
LL md;
int first[200001],f[200001],p[2000001],vis[200001];
LL dis[200001],d[200001];
void add(int x,int y,LL z){e[tot].s=x;e[tot].t=y;e[tot].v=z;e[tot].nxt=first[x];first[x]=tot++;};
int bfs(int u) //寻找最远的点
{
cnt=0;
for(int i=1;i<=n;i++) dis[i]=-1;
queue<int> q;
q.push(u);
dis[u]=0;
LL maxd=0;
int pos=u;
while(!q.empty())
{
int tp=q.front();
q.pop();
if(dis[tp]>maxd)
{
maxd=dis[tp];
pos=tp;
}
for(int i=first[tp];i!=-1;i=e[i].nxt)
{
if(dis[e[i].t] == -1)
{
dis[e[i].t]=dis[tp]+e[i].v;
q.push(e[i].t);
p[e[i].t]=tp;
}
}
}
return pos;
}
void find(int x,int fa)
{
if(md<d[x]) md=d[x];
for(int i=first[x];i!=-1;i=e[i].nxt)
{
if(e[i].t!=fa && vis[e[i].t]!=1)
{
d[e[i].t]=d[x]+e[i].v;
find(e[i].t,x);
}
}
}
int main()
{
int a,b,left=0,right=0;
LL c;
scanf("%d",&n);
memset(first,-1,sizeof(first));
memset(vis,0,sizeof(vis));
memset(p,0,sizeof(p));
for(int i=1;i<=n-1;i++)
{
scanf("%d%d%lld",&a,&b,&c);
add(a,b,c);
add(b,a,c);
}
int low=bfs(b);
int high=bfs(low); //起点终点 p【i】表示中间的点
printf("%lld\n",dis[high]);
for(int i=high;i!=low;i=p[i])vis[i]=1;
vis[low]=1;
int l=low;
int r=high;
bool flag=true;
for(int i=high;i!=low;i=p[i])
{
LL ld=dis[i];
LL rd=dis[high]-dis[i];
md=0;
find(i,0);
if(md == ld && flag == 1) {l=i;flag=0;}
if(md == rd) {r=i;}
}
for(int i=r;i!=l;i=p[i]) ans++;
printf("%d\n",ans);
return 0;
}