思路

图可能会有多个连通块,整个图最多有一个环。 基环树的题目,对每一块找环切断,然后树形dp。 环的切断方式有两种,从那条边的起始点出发做两次dp, 当前块的答案就加上两次dp结果的较大值。

代码

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define int long long
using namespace std;
const int maxn=1e6+7;

//int read(){	int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}

struct Edge{
	int nxt,to;
}e[maxn];

int n,deg[maxn],vis[maxn],mrky,mrke;
int f[maxn][2],ans,tmp;
int a[maxn],v[maxn];
int head[maxn],cnt=0;

inline void addedge(int u,int v){
	e[++cnt]=(Edge){head[u],v};
	head[u]=cnt;
}

void dp(int x){
	vis[x]=1;
	f[x][1]=a[x];f[x][0]=0;
	for(int i=head[x];i;i=e[i].nxt){
		int y=e[i].to;
		if(y==mrky){ 
			f[y][1]=-inf;
			continue;
		}
		dp(y);
		f[x][0]+=max(f[y][0],f[y][1]);
		f[x][1]+=f[y][0];
	}
}

void find_loop(int x){
	vis[x]=1;
	if(vis[v[x]]) mrky=x; //讨厌的人已经被选了 
	else find_loop(v[x]);
	return;
}

signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i]>>v[i];
		addedge(v[i],i);
	}
	for(int i=1;i<=n;i++){
		if(vis[i]) continue;
		find_loop(i);
		dp(mrky);
		tmp=max(f[mrky][0],f[mrky][1]);
		mrky=v[mrky];
		dp(mrky);
		ans+=max(tmp,max(f[mrky][0],f[mrky][1]));
	}
	cout<<ans<<"\n";
	return 0;
}