题目大意:
给定基环外向树森林,每个点有点权,一条边连接的两个点不能同时选取,问选取的点权和最大。
题解:
如果是一棵树,有一个显然树形DP。
如果是基环外向树,那么先在每棵树上DP再在环上DP。
然而这个做法比较麻烦。
于是我们断开环上的一条边,强制一个点不选,就变成了一棵树。
跑两遍树形DP即可
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int flag,n,x,l,r,cnt,tot,last[2000005],vis[2000005],s[2000005];
long long ans,f[2000005][2],val[2000005];
struct node{
int to,next;
}e[2000005];
void add(int a,int b){
e[++cnt].to=b;
e[cnt].next=last[a];
last[a]=cnt;
}
void dfs(int x,int fa){
vis[x]=1;
for (int i=last[x]; i!=-1 && (!flag); i=e[i].next){
int V=e[i].to;
if (V!=fa){
if (vis[V]){
l=x,r=V;
s[i]=s[i^1]=-1;
flag=1;
break;
}
dfs(V,x);
}
}
}
void tree_dp(int x,int fa,int ed){
vis[x]=1;
if (x!=ed) f[x][1]=val[x];
else f[x][1]=0;
f[x][0]=0;
for (int i=last[x]; i!=-1; i=e[i].next){
int V=e[i].to;
if (V==fa) continue;
if (s[i]==-1) continue;
tree_dp(V,x,ed);
f[x][0]+=max(f[V][0],f[V][1]);
f[x][1]+=f[V][0];
}
}
int main(){
cnt=-1;
memset(last,-1,sizeof(last));
int n;
scanf("%d",&n);
for (int i=1; i<=n; i++){
int x;
scanf("%lld%d",&val[i],&x);
add(x,i);
add(i,x);
}
for (int i=1; i<=n; i++)
if (!vis[i]){
flag=0;
dfs(i,0);
long long maxx=0;
tree_dp(l,0,r);
maxx=max(f[l][0],f[l][1]);
tree_dp(r,0,l);
maxx=max(maxx,max(f[r][0],f[r][1]));
ans+=maxx;
}
printf("%lld\n",ans);
return 0;
}