[ZJOI2008]骑士
这题其实就是没有上司的舞会在基环树上的扩展。
那么做法也差不多,对于基环树,在环上任选一条边删掉。
由于这条边连接的两个点不能同时选,于是硬点一个点不选,以另一个点为根做一遍dp,这样做两次dp之后取个最大值就是答案。
这题有可能是个森林,于是对于每个基环树做一遍加起来就行了。
#include<cmath>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define int long long
#define maxn 2000050
void read(int &x) {
x=0;int f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);x*=f;
}
void print(int x) {
if(x<0) putchar('-'),x=-x;
if(!x) return ;print(x/10),putchar((x%10)^48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}
const int mod = 1e9+7;
int n,m,f[2][maxn][2],head[maxn],tot,v[maxn],vis[maxn],RT,ff[maxn],rt;
struct edge {int to,nxt;}e[maxn<<1];
void ins(int u,int v) {e[++tot].to=v,e[tot].nxt=head[u],head[u]=tot;}
void dp(int k,int x,int pre) {
f[k][x][1]=v[x];vis[x]=1;
for(int i=head[x];i;i=e[i].nxt) {
if(e[i].to==pre) {f[k][e[i].to][1]=-1e9;continue;}
dp(k,e[i].to,pre);
f[k][x][0]+=max(f[k][e[i].to][1],f[k][e[i].to][0]);
f[k][x][1]+=f[k][e[i].to][0];
}
}
int calc(int x) {
while(!vis[x]) vis[x]=1,x=ff[x];
dp(0,x,x);dp(1,ff[x],ff[x]);
//for(int i=1;i<=n;i++) printf("%lld %lld\n",f[1][i][0],f[1][i][1]);
return max(max(f[0][x][1],f[0][x][0]),max(f[1][ff[x]][0],f[1][ff[x]][1]));
}
void solve() {
read(n);int x,ans=0;
for(int i=1;i<=n;i++) read(v[i]),read(x),ins(x,i),ff[i]=x;
for(int i=1;i<=n;i++) if(!vis[i]) ans+=calc(i);
write(ans);
}
signed main() {solve();return 0;}