借题区别了一下环套树和仙人掌
常见做法是拆环做2次DP
邻接表的话也能做,vis一下封堵点,第二次再vis到放行就好
//#include<bits/stdc++.h>
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<stdio.h>
#include<algorithm>
#include<queue>
#include<string.h>
#include<iostream>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<iomanip>
using namespace std;
#define ll long long
#define pb push_back
#define FOR(a) for(int i=1;i<=a;i++)
const int inf=0x3f3f3f3f;
const int maxn=1e6+5;
ll a[maxn];
int n;
struct EDGE{
int u,v,nxt;
}G[maxn<<1];
int tot,head[maxn];
void init(){tot=0;memset(head,-1,sizeof head);}
void addedge(int u,int v){
G[tot]=(EDGE){u,v,head[u]};head[u]=tot;tot++;
G[tot]=(EDGE){v,u,head[v]};head[v]=tot;tot++;
}
bool s[maxn<<1];
ll f[maxn][2];
bool flag;
int vis[maxn];
int l,r;
void dfs(int u,int fa){
vis[u]=1;
for(int i=head[u];~i && !flag;i=G[i].nxt){
if(G[i].v!=fa){
if(vis[G[i].v]){
l=u,r=G[i].v;
s[i]=s[i^1]=1;
flag=1;
break;
}
dfs(G[i].v,u);
}
}
}
void dp(int u,int fa,int fob){
vis[u]=1;
if(u!=fob)f[u][1]=a[u];
else f[u][1]=0;
f[u][0]=0;
for(int i=head[u];~i;i=G[i].nxt){
if(G[i].v!=fa && !s[i]){
dp(G[i].v,u,fob);
f[u][0]+=max(f[G[i].v][0],f[G[i].v][1]);
f[u][1]+=f[G[i].v][0];
}
}
}
int main(){
scanf("%d",&n);
init();
for(int i=1,x;i<=n;i++){
scanf("%lld%d",&a[i],&x);
addedge(i,x);
}
ll ans=0;
for(int i=1;i<=n;i++){
if(!vis[i]){
flag=false;
dfs(i,0);
ll tmp=0;
dp(l,0,r);
tmp=max(f[l][0],f[l][1]);
dp(r,0,l);
tmp=max(tmp,max(f[r][0],f[r][1]));
ans+=tmp;
}
}
printf("%lld\n",ans);
}