传送门

显然题目给的图构成一个基环树森林

对于每个基环树单独考虑,显然每个都走直径是最优的

考虑如何求出基环树的直径

把直径分为两种情况考虑,首先可以找出环

因为直径可能不在环边上,所以对每个环上节点的子树进行一遍 $dfs$,求出每个节点子树的直径

维护 $dis[x]$ 表示节点 $x$ 到叶子节点的最长路程,那么直径就是每个节点儿子的 $dis$ 中最大和次大的和

可以一遍循环动态维护最大和次大

直径也可能在环上

设环上两点 $x,y$ 的距离为 $d(x,y)$,那么就是求最大的 $dis[x]+dis[y]+d(x,y)$

这样复杂度是 $O(n^2)$,考虑优化

按照套路,考虑把环断成链:

维护一条链上的距离前缀和 $sum[\ ]$,设 $y$ 在 $x$ 后面,那么就是求直径就是 $dis[x]+dis[y]+sum[y]-sum[x]$

换一下,就是求对于每一个 $y$,求链上区间 $x\in(y-n,y)$ 的 $(dis[x]-sum[x])$ 最大值 $+(dis[y]+sum[y])$($n为环的节点数$)

显然这个东西我们可以单调队列优化

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
inline int read()
{
int x=,f=; char ch=getchar();
while(ch<''||ch>'') { if(ch=='-') f=-; ch=getchar(); }
while(ch>=''&&ch<='') { x=(x<<)+(x<<)+(ch^); ch=getchar(); }
return x*f;
}
const int N=1e6+;
int fir[N],from[N<<],to[N<<],val[N<<],cntt;
inline void add(int a,int b,int c)
{
from[++cntt]=fir[a]; fir[a]=cntt;
to[cntt]=b; val[cntt]=c;
}
struct edge{
int v,w;//节点,边权
}To[N];//To存题目给出的数据
int n,tot,ring[N],d[N];//ring存环上节点,d存环上节点到下一环上节点的边的边权
ll ANS;
int fa[N];//存环节点的'父'节点
bool vis[N],p[N];//vis判断是否为走过的基环树节点,p判断是否是基环树环节点
void BFS(int x)//找环
{
tot=; vis[x]=; int t;
while()
{
t=To[x].v;
if(vis[t])//找到就一路回跳并更新ring,d,p
{
ring[++tot]=t; d[tot]=To[t].w; p[t]=;
for(int i=x;i!=t;i=fa[i])
p[i]=,ring[++tot]=i,d[tot]=To[i].w;
return;
}
vis[t]=; fa[t]=x; x=t;//否则就继续找
}
}
ll dis[N],res;//维护当前基环树直径
void dfs(int x,int f)//处理dis和子树直径最大值
{
vis[x]=;
for(int i=fir[x];i;i=from[i])
{
int &v=to[i]; if(p[v]||v==f) continue;
dfs(v,x); res=max(res,dis[x]+dis[v]+val[i]);
//此时dis[x]还没有dis[v]+val[i],所以res可以这样更新
dis[x]=max(dis[x],dis[v]+val[i]);//更新dis
}
}
int Q[N<<]; ll sum[N<<];
inline int id(int x) { return (x-)%tot+; }//把链节点换成环节点
inline ll calc(int x) { return dis[ring[id(x)]]-sum[x]; }
inline void solve()//单调队列
{
int l=,r=;
for(int i=;i<=(tot<<);i++)
{
sum[i]=sum[i-]+d[id(i)];
while(l<=r && i-Q[l]>=tot ) l++;
if(l<=r) res=max(res,calc(Q[l])+sum[i]+dis[ring[id(i)]]);//先更新res
while(l<=r && calc(i)>=calc(Q[r]) ) r--;//再更新队列
Q[++r]=i;
}
}
int main()
{
n=read(); int a,b;
for(int i=;i<=n;i++)
{
a=read(),b=read();
add(i,a,b); add(a,i,b);
To[i].v=a; To[i].w=b;
}
for(int i=;i<=n;i++)
{
if(vis[i]) continue;
BFS(i); res=;
for(int j=;j<=tot;j++) dfs(ring[j],);
solve(); ANS+=res;
}
printf("%lld",ANS);
return ;
}

最新文章

  1. vCSA加域&amp;vcenter关联域&amp;设置管理员权限
  2. jquery.nicescroll完美滚动条使用方法
  3. php生成网页桌面快捷方式
  4. 预定义接口-迭代器Iterator
  5. 2016-07-15: Window定时器使用
  6. win7 解锁注册表
  7. 对CAB文件进行数字签名
  8. POJ1038 - Bugs Integrated, Inc.(状态压缩DP)
  9. _sntprintf_s 和 _sntprintf 区别
  10. cut sticks
  11. 【管用】 使用VMtools实现主机Windows与虚拟机Linux文件共享
  12. ESP-EYE V2.1 开发板 WINDOWS 10 开发入门
  13. PHP常用类------生成验证码类Code
  14. 总结一下搭建简单Web服务器的一些方法
  15. java 多维数据定义
  16. Sublime text —— 自定义主题Soda
  17. [UE4]添加蒙太奇动画
  18. 18年11月5日 NOIP模拟赛
  19. 基于CSS3 3D百叶窗图像过渡特效
  20. 当Appium中遇到alert(python篇)

热门文章

  1. 443. String Compression字符串压缩
  2. ESP8266-iot-简介1
  3. Codeforces 429B B. Working out
  4. mybatis 获得一个map的返回集合
  5. Claims Based Authentication and Token Based Authentication和WIF
  6. 解决iReport打不开的一种方法
  7. SQL之DML
  8. MongoDB整理笔记のDump &amp; Restore
  9. jQuery bind() live()
  10. Java java.lang.Thread#join()方法分析