题意:给你n个点n条边,然后找到一个集合(满足任何两个点之间没边)并且这个集合sum最大。输出sum
解:看题面觉得是最大独立集,但是范围太大-->考虑树形dp
1、画图可知n点构成多个基环树
2、因为只是一条边上的两个点不能同时选。所以考虑找到基环树上的环,然后断掉这个环那么从断掉的两个点分别做dp
有变量f[x],g[x] 分别表示: 选上这个点的的情况下考虑子树的情况下的最大值 和不选这个点的情况下,子树总和的最大值
那么我们每次 ans+=max(g[u] |dp(u,0) ,g[v]|dp(v,0));表示一个基环树里的最优情况一定(要么不选U,V任意,要么不选V,U任意)
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define en '\n'
#include<queue>
#define ll long long
const int maxn = 1000000+10;
using namespace std;
struct node{
int v,nxt;
}edge[maxn<<1];
ll h[maxn],a[maxn],f[maxn],g[maxn];
bool vis[maxn];
int tot;
void add(int x,int y){
edge[++tot]=(node){y,h[x]};h[x]=tot;
}
int U,V,E;
void dfs(int x,int from){
vis[x]=1;
#define int register int
for(int i=h[x];i;i=edge[i].nxt){
if((i^1)==from)continue;
int y=edge[i].v;
if(vis[y]){
E=i,U=x,V=y;
continue;
}
dfs(y,i);
}
}
void dp(int x,int from){
g[x]=0,f[x]=a[x];
for(int i=h[x];i;i=edge[i].nxt){
if(i==E or (i^1)==E)continue;
if((i^1)==from)continue;
dp(edge[i].v,i);
f[x]+=g[edge[i].v];
g[x]+=max(f[edge[i].v],g[edge[i].v]);
}
}
signed main(){
#ifdef local
freopen("input2.txt","r",stdin);
#endif // local
int n;
cin>>n;tot=1;
for(int i=1;i<=n;i++){
int x;scanf("%d%d",&a[i],&x);add(i,x),add(x,i);
}
ll ans=0;
for(int i=1;i<=n;i++){
if(vis[i])continue;
dfs(i,0);
dp(U,0);
ll tem=g[U];
dp(V,0);
ans+=max(g[V],tem);
}cout<<ans<<en;
}