由于每个点只能向外连一条边,\(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;
}
标签:IOI2008,rt,1000010,int,Island,sum,max1,max,P4381
From: https://www.cnblogs.com/UserJCY/p/18536352/jt_pseudotree_P4381