题目
最近想要刷博客数,给自己一种努力的假象,最好的方式当然是给做过的题目做一篇题解啦
标准树形DP题,选了父节点不能选子节点,唯一难点是基环树的处理
题解
先找环,只需要找到环上的两个点就行了,其他点不用一起找出,用dfs,断边
断边的具体操作是:分别将边上的两个点设为根进行树形DP,但两个点之间还有的关系怎么办呢,就每一次强制不选根节点,然后比较两次DP的结果,选取最大的那个
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int M=1000005;
#define INF 20000000
struct E
{
int nxt,to;
}edge[M];
int head[M],fa[M],n,cnt=0,root,vis[M];
ll val[M],dp[M][2],ans1=0;
void add_edge(int from,int to)
{
edge[++cnt].to=to;
edge[cnt].nxt=head[from];
head[from]=cnt;
}
void shudp(int m)
{
vis[m]=1;
dp[m][0]=0;dp[m][1]=val[m];
int go;
for(int i=head[m];i;i=edge[i].nxt)
{
go=edge[i].to;
if(go!=root)
{
shudp(go);
dp[m][0]+=max(dp[go][0],dp[go][1]);
dp[m][1]+=dp[go][0];
}
else
{
dp[go][1]=-INF;
}
}
}
void find_root(int m)
{
vis[m]=1;
while(vis[fa[m]]!=1)
{
m=fa[m];
vis[m]=1;
}
root=m;
shudp(root);
ll t=max(dp[m][0],dp[m][1]);
root=fa[root];
shudp(root);
ans1+=max(t,max(dp[root][1],dp[root][0]));
}
int main()
{
scanf("%d",&n);
int x1,x2;
for(int i=1;i<=n;i++)
{
scanf("%d%d",&x1,&x2);
val[i]=x1;
fa[i]=x2;
add_edge(x2,i);
}
for(int i=1;i<=n;i++)
{
if(vis[i]==0)
{
find_root(i);
}
}
cout<<ans1<<endl;
}