题目大意:给你一个基环树林,每个点有点权。子节点和父节点不能同时选,求最大价值
题解:题目可以转化为最大点独立集,但网络流肯定超时。考虑树形dp。一般来说,基环树dp可以先按照树形dp写出转移方程,然后特殊处理环的情况。而处理环的话可以把环破开成为链。
用f[x][0/1]表示x为根的子树不选/选x得到的最大收益,转移显然……
注意:题目中是无向边,因为a不喜欢b的话不能同时选ab,语文不好容易GG……
我的收获:特殊处理
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int M=1000005;
int n,t;
int R1,R2,ban,R;
int head[M],a[M];
long long dp[M][2];
bool vis[M];
struct edge{int to,nex;}e[M*2];
void add(int u,int v){e[t].to=v,e[t].nex=head[u],head[u]=t++;}
void dfs(int x,int fa){
vis[x]=1;
for(int i=head[x];i!=-1;i=e[i].nex){
int v=e[i].to;
if(v==fa) continue;
if(vis[v]) R1=x,R2=v,ban=i;//找到环边和端点
else dfs(v,x);
}
}
void solve(int x,int fa){
dp[x][0]=0;dp[x][1]=a[x];
if(x==R) dp[x][1]=-1e16;
for(int i=head[x];i!=-1;i=e[i].nex)
if(i!=ban&&(i!=(ban^1))){//无向边
int v=e[i].to;
if(v!=fa){
solve(v,x);
dp[x][0]+=max(dp[v][0],dp[v][1]);
dp[x][1]+=dp[v][0];
}
}
}
void work()
{
long long tmp,ans=0;
for(int i=1;i<=n;i++)
if(!vis[i]){//处理每个联通块
dfs(i,0);
R=R1;solve(i,0);tmp=max(dp[i][0],dp[i][1]);//强制R为根且R不选,剩下的随便选
R=R2;solve(i,0);ans+=max(tmp,max(dp[i][0],dp[i][1]));
}
printf("%lld",ans);
}
void init()
{
cin>>n;t=0;
memset(head,-1,sizeof(head));
for(int i=1,x;i<=n;i++)
scanf("%d%d",&a[i],&x),add(i,x),add(x,i);
}
int main()
{
init();
work();
}