P2607(基环树+树形DP)

题目

最近想要刷博客数,给自己一种努力的假象,最好的方式当然是给做过的题目做一篇题解啦
标准树形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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值