通过题面 以及样例我们可以分析出 这道题的数据是一个基环树森林 而对于本题来说,在相同的基环树中走路,在不同的基环树间划船
因此这道题可以简化为:在基环树森林中 找到所有基环树直径之和的最大值
那么如何找基环树的直径呢?
预备工作:找到基环树中的环,用\(sta\)来储存
首先规定maxx为储存当前的最大值 我们可以将其分为两种情况:
\(1.\)没有经过环
这个时候相当于找将环删除后的每一棵子树的直径 这个时候可以用\(dp\)来找到 用\(d\)来存每一个点在不经过环的情况下的直径最大值(这样说可能有误,但意思就是这个!)
\(2.\)经过了环
这种情况 我们首先需要用一个\(sum\)数组来储存在这个环上的边权的前缀和 然后对于两个点\(i<j\) 他们所形成的路径的值为:\(d[i]+d[j]+sum[j-1]-sum[i-1]\) 注意这里\(sum\)里面-1了 因为我在处理前缀和的时候就相当于向前挪了一位
当j一定的时候 i,j形成的路径的值为\(d[j]+sum[j-1]+d[i]-sum[i-1]\) 用锐利的眼睛 你会惊奇的发现: 如果要使这个值最大 你应该使得 \(d[i]-sum[i-1]\)尽量大 这个时候就可以用单调队列维护一个单调递减的序列,进行优化了!
细节:在进行环上的dp时 将环拆开 然后double it
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define int long long
using naespace std;
const int maxn=1000010;
int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
int n,first[maxn],next[maxn<<1],to[maxn<<1],val[maxn<<1];
int sta[maxn],top,tot=1,fa[maxn];
int maxx,d[maxn];
bool in_ring[maxn];
void add(int x,int y,int z)
{
tot++;
next[tot]=first[x];
first[x]=tot;
to[tot]=y;
val[tot]=z;
}
int dfn[maxn],times;
void dfs1(int x,int father)//找环
{
dfn[x]=++times;
for(int i=first[x];i;i=next[i])
{
int y=to[i];
if(y==father) continue;
if(!dfn[y])
{
fa[y]=x;
dfs1(y,x);
}
else if(dfn[y]>dfn[x])
{
for(int j=y;j!=x;j=fa[j])
sta[++top]=j,in_ring[j]=true;
sta[++top]=x,in_ring[x]=true;
}
}
}
void dp(int x,int fa)//计算d数组
{
for(int i=first[x];i;i=next[i])
{
int y=to[i];
if(y==fa || in_ring[y]) continue;
dp(y,x);
maxx=max(maxx,d[x]+d[y]+val[i]);
d[x]=max(d[x],d[y]+val[i]);
}
}
int lian[maxn<<1],sum[maxn<<1];
int double_top;
void dfs2(int x,int pos,int toward)//计算sum数组
{
if(pos==double_top) return ;
for(int i=first[x];i;i=next[i])
{
int y=to[i];
if(lian[pos+1]==y && i!=toward)
{
sum[pos]=sum[pos-1]+val[i];
dfs2(y,pos+1,i^1);//这里i^1 相当于这条边的反向边 下次不能走这条边
return ;
}
}
}
int q[maxn<<1],head,tail,ans=0;
void work(int x)
{
top=0;maxx=0;
dfs1(x,0);
for(int i=1;i<=top;i++)
dp(sta[i],0);
for(int i=1;i<=top;i++)
lian[i]=lian[i+top]=sta[i];
double_top=top*2;
dfs2(sta[1],1,0);
head=1;tail=0;
for(int i=1;i<=double_top;i++)//用单调队列优化
{
while(head<=tail && i-q[head]>=top)
head++;
if(head<=tail && sum[i-1]-sum[q[head]-1]+d[lian[i]]+d[lian[q[head]]]>maxx)
maxx=sum[i-1]-sum[q[head]-1]+d[lian[i]]+d[lian[q[head]]];
while(head<=tail && (i-q[tail]>=top || d[lian[i]]-sum[i-1]>d[lian[q[tail]]]-sum[q[tail]-1]))
tail--;
q[++tail]=i;
}
ans+=maxx;
}
int main()
{
n=read();
for(int i=1;i<=n;i++)
{
int x=read(),y=read();
add(i,x,y);add(x,i,y);
}
for(int i=1;i<=n;i++)
{
if(!dfn[i])
work(i);
}
printf("%lld",ans);
}