题面传送门
这是一道基环树直径的题目。
考虑对于一个基环树,只有两种情况:直径在环上,直径在半个环上与链上。
直径在环上直接搞就好了,另一种情况可以单调队列优化dp
代码实现:
#include<cstdio>
#include<cstring>
#define max(a,b) ((a)>(b)?(a):(b))
using namespace std;
int n,m,k,x,y,z,st,flag[1000039],flag2[1000039],q[2000039],head,tail,f[1000039],fh;
long long ans,dp[1000039],tot,sf[1000039],pus,d[1000039];
struct yyy{int to,w,z;}tmp;
struct ljb{
int head,h[1000039];
yyy f[2000039];
inline void add(int x,int y,int z){
f[++head]=(yyy){y,z,h[x]};
h[x]=head;
}
}s;
inline int dfs(int x,int last){
if(flag[x]==1){
flag[x]=2;f[++fh]=x;flag2[x]=1;
return 1;
}
flag[x]=1;
int cur=s.h[x];
yyy tmp;
while(cur!=-1){
tmp=s.f[cur];
if(cur!=((last-1)^1)+1&&dfs(tmp.to,cur)) {
if(flag[x]!=2)f[++fh]=x,flag2[x]=1,sf[fh]=sf[fh-1]+tmp.w;
else{sf[st-1]=sf[st]-tmp.w;return 0;}
return 1;
}
cur=tmp.z;
}
return 0;
}
inline void tdp(int x){
flag2[x]=1;
int cur=s.h[x];
yyy tmp;
while(cur!=-1){
tmp=s.f[cur];
if(!flag2[tmp.to]) {
tdp(tmp.to);
pus=max(pus,d[x]+d[tmp.to]+tmp.w);
d[x]=max(d[x],d[tmp.to]+tmp.w);
}
cur=tmp.z;
}
}
inline void read(int &x){
char s=getchar();x=0;
while(s<'0'||s>'9') s=getchar();
while(s>='0'&&s<='9') x=(x<<3)+(x<<1)+(s^48),s=getchar();
}
int main(){
memset(s.h,-1,sizeof(s.h));
register int i,j;
scanf("%d",&n);
for(i=1;i<=n;i++) read(x),read(z),s.add(x,i,z),s.add(i,x,z);
for(i=1;i<=n;i++){
if(!flag2[i]){
st=fh+1;
dfs(i,0);
tot=0;
for(j=st;j<=fh;j++){
pus=0;
tdp(f[j]);
tot=max(tot,pus);
dp[j+fh-st+1]=dp[j]=d[f[j]];
sf[j+fh-st+1]=sf[j+fh-st]+sf[j]-sf[j-1];
}
head=tail=0;pus=0;
for(j=st;j<=2*fh-st+1;j++){
while(head!=tail&&q[head+1]<=j-fh+st-1) head++;
if(head!=tail)pus=max(pus,dp[j]+dp[q[head+1]]+sf[j]-sf[q[head+1]]);
while(head!=tail&&dp[q[tail]]-sf[q[tail]]<=dp[j]-sf[j]) tail--;
q[++tail]=j;
}
ans+=max(pus,tot);
//printf("%lld %lld\n",tot,pus);
}
}
printf("%lld\n",ans);
}