luogu P3304 [SDOI2013]直径

树的直径两遍dfs救星了

至于一定在直径中的边数,可以发现这些边一定是连续的(不然你两条直径中间能有空挡?),然后,如果某个点往下有多条直径,那么这条点以下都不算入答案.所以以直径分别两端点为根,找出这样离根最近的点(注意最远的点就是另一个端点),然后找到那两个点的距离就是答案

// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define LL long long
#define il inline
#define re register
#define db double
#define max(a,b) ((a)>(b)?(a):(b))

using namespace std;
const int N=200000+10;
il LL rd()
{
    re LL x=0,w=1;re char ch=0;
    while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    return x*w;
}
int to[N<<1],nt[N<<1],hd[N],tot=1;
LL w[N<<1],di[N],f[N],lent;
bool v[N];
il void add(int x,int y,LL z)
{
  ++tot;to[tot]=y;nt[tot]=hd[x];w[tot]=z;hd[x]=tot;
  ++tot;to[tot]=x;nt[tot]=hd[y];w[tot]=z;hd[y]=tot;
}
int n,fa[N];
il int dfs(int x,int ffa)
{
  int xx=0;
  for(int i=hd[x];i;i=nt[i])
    {
      int y=to[i];
      if(y==ffa) continue;
      fa[y]=x,di[y]=di[x]+w[i];
      int yy=dfs(y,x);
      if(di[xx]<di[yy]) xx=yy;
    }
  return xx?xx:x;
}
il int dd(int x,int ffa)
{
  f[x]=0;
  int xx=0,cnt=0;
  for(int i=hd[x];i;i=nt[i])
    {
      int y=to[i];
      if(y==ffa) continue;
      int yy=dd(y,x);
      if(!xx) xx=yy;
      f[x]=max(f[x],f[y]+w[i]);
      if(di[x]+f[y]+w[i]==lent&&ffa) ++cnt;
    }
  return (cnt>1&&v[x])?x:xx;
  
}

int main()
{
  int aa,bb,c,d,an=0;
  n=rd();
  for(int i=1;i<n;i++)
    {
      int x=rd(),y=rd(),z=rd();
      add(x,y,z);
    }
  aa=1;
  di[1]=fa[1]=0;aa=dfs(1,0);
  di[aa]=fa[aa]=0;bb=dfs(aa,0);
  lent=di[bb];
  int now=bb;
  while(now) v[now]=true,now=fa[now];
  c=dd(aa,0);if(!c) c=bb;
  di[bb]=fa[bb]=0,dfs(bb,0);
  d=dd(bb,0);if(!d) d=aa;
  while(d!=c) ++an,d=fa[d];
  printf("%lld\n%d\n",lent,an);
  return 0;
}

猜你喜欢

转载自www.cnblogs.com/smyjr/p/9544922.html