这道题没仔细去想直接点开的题解,看了一会儿后发现是基环森林求直径的模板题,于是自己码了一波。
对于一颗树我们可以直接 d f s dfs dfs求出直径,看题解求直径竟然只保存了最大值,觉得以前求直径保存最大和次大太low了,对于一看基环树,首先求出环的子树中的最大路径设为 s i s_i si,接着把环的序列倍长(套路),求出环上从序列第一个点到其他点的路径长度前缀和,设为 s u m i sum_i sumi,那么现在要做的即求出 i , j ( i < j ) i,j ~~ (i < j) i,j (i<j)满足 a b s ( i , j ) ≤ n abs(i,j) \leq n abs(i,j)≤n使得 s i + s j + s u m j − s u m i s_i+s_j+sum_j-sum_i si+sj+sumj−sumi最大,那么考虑枚举 i i i,现在就是要求 s j + s u m j s_j+sum_j sj+sumj最大,这个显然可以用单调队列来维护。时间复杂度为 O ( n ) O(n) O(n)。
/*******************************
Author:galaxy yr
LANG:C++
Created Time:2019年10月20日 星期日 14时57分32秒
*******************************/
#include
#include
#include
using namespace std;
struct IO{
template<typename T>
IO & operator>>(T&res)
{
T q=1;char ch;
while((ch=getchar())<'0' or ch>'9')if(ch=='-')q=-q;
res=(ch^48);
while((ch=getchar())>='0' and ch<='9') res=(res<<1)+(res<<3)+(ch^48);
res*=q;
return *this;
}
}cin;
struct edge{
int to,next,w;
edge(int a=0,int b=0,int c=0):to(a),next(b),w(c){}
};
const int maxn=1e6+10;
long long n,head[maxn],cnt=1,vis[maxn],stk[maxn],top,ans,d[maxn],res,sum[maxn*2],s[maxn*2];
bool is_circle[maxn];
edge e[maxn<<1];
deque<int>que;
inline void add(int u,int v,int w)
{
e[++cnt]=edge(v,head[u],w);
head[u]=cnt;
}
bool dfs(register int now,register int la)
{
vis[now]++;
if(vis[now]==2) //出现两次为环,作为环的起点
{
stk[++top]=now;
is_circle[now]=1;
return 1;
}
for(register int i=head[now];i;i=e[i].next)
if((i^1)!=la && dfs(e[i].to,i))
{
if(vis[now]!=2) //不是起点
{
stk[++top]=now,is_circle[now]=1,s[top]=e[i].w;
}
else //起点
{
s[1]=e[i].w;
return 0;
}
return 1;
}
return 0;
}
void dp(register int now,register int fa)
{
vis[now]=1;
for(register int i=head[now];i;i=e[i].next)
if(e[i].to!=fa)
{
if(is_circle[e[i].to]) continue;
dp(e[i].to,now);
ans=max(ans,d[now]+d[e[i].to]+e[i].w);
d[now]=max(d[now],d[e[i].to]+e[i].w);
}
}
bool check(int i,int j)//比较函数
{
return s[i]+sum[i]<=s[j]+sum[j];
}
void push(int i)//单调队列压入i
{
while(!que.empty() && check(que.back(),i))
que.pop_back();
que.push_back(i);
}
void pop(int i)//单调队列弹出不满足条件的
{
while(!que.empty() && abs(i-que.front())>n || i==que.front())
que.pop_front();
}
long long solve(int rt)
{
ans=top=0;
dfs(rt,0);
while(!que.empty())que.pop_back();
if(top)//有环
{
for(int i=1;i<=top;i++)
dp(stk[i],0);
sum[0]=sum[1]=0;
for(int i=2;i<=2*top;i++)
sum[i]=sum[i-1]+s[(i-1)%top+1];
for(int i=1;i<=2*top;i++)
s[i]=d[stk[(i-1)%top+1]];
for(int i=2;i<=top;i++)
push(i);
for(int i=1;i<=top;i++)
{
pop(i);
ans=max(ans,s[i]+s[que.front()]+sum[que.front()]-sum[i]);
push(i+top);
}
}
else//一棵树,好像没有这种情况
{
dp(rt,0);
ans+=d[rt];
}
return ans;
}
int main()
{
//freopen("p4381.in","r",stdin);
//freopen("p4381.out","w",stdout);
cin>>n;
register int v,w;
for(register int i=1;i<=n;i++)
{
cin>>v>>w;
add(i,v,w);
add(v,i,w);
}
for(register int i=1;i<=n;i++)
if(!vis[i])
res+=solve(i);
printf("%lld\n",res);
return 0;
}