Loading

P4381 [IOI2008] Island 基环树

P4381 [IOI2008] Island

由于每个点只能向外连一条边,\(n\) 个点 \(n\) 条边,中间有环,故不能再向外连边,所以构成基环内向树森林。

叶子节点入度为 \(0\),故可以判断叶子结点,倒推回环根,存每个子树的最大深度。

最终 dp 处理每个基环树的环,分两种情况:

  • 经过环:分两种情况:顺时针和逆时针,前缀和优化跑一边即可。
  • 不经过环:答案为子树直径
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
int a[1000010],w[1000010];
int ind[1000010];
queue<int> q;
int l[1000010],d[1000010];
bool vis[1000010];
int ans;
int max(int a,int b,int c){
	return max(a,max(b,c));
}
int solve(int rt){
	int p=rt;
	int max1=l[rt],max2=l[rt],ret1=0,ret2=-2147483648,ret3=d[rt];
	int sum=w[rt];
	while((p=a[p])!=rt){
		vis[p]=1;
		ret1=max(ret1,l[p]+sum+max1);
		ret2=max(ret2,l[p]-sum+max2);
		ret3=max(ret3,d[p]);
		max1=max(max1,l[p]-sum);
		max2=max(max2,l[p]+sum);
		sum+=w[p];
	}
	return max(ret1,ret2+sum,ret3);
}
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i]>>w[i];
		ind[a[i]]++;
	}
	for(int i=1;i<=n;i++)if(!ind[i])q.push(i);
	while(!q.empty()){
		int u=q.front();q.pop();
		int v=a[u];
		d[v]=max(d[v],d[u],l[v]+l[u]+w[u]);
		l[v]=max(l[v],l[u]+w[u]);
		ind[v]--;
		if(!ind[v])q.push(v);
	}
	for(int i=1;i<=n;i++){
		if(ind[i]&&!vis[i])ans+=solve(i);
	}
	cout<<ans;
	return 0;
}
posted @ 2024-11-09 09:58  UserJCY  阅读(39)  评论(0)    收藏  举报
Title