P10933 创世纪 题解

题目传送门

前置知识

树形 DP

解法

\(a_{i}\)\(i\) 连一条有向边,这样就形成了基环外向树森林。

\(f_{x,0/1}\) 表示 \(x\) 不选/选时,以 \(x\) 为根的子树的最多选择个数,状态转移方程为 \(\begin{cases} f_{x,0}=\sum\limits_{y \in Son(x)} \max(f_{y,0},f_{y,1}) \\ f_{x,1}=1+\max\limits_{y \in Son(x)} \{ f_{y,0}+\sum\limits_{z \in Son(x),z \ne y} \max(f_{z,0},f_{z,1}) \} \end{cases}\),其中 \(f_{x,1}\) 的转移可以进一步写作 \(f_{x,1}=1+f_{x,0}- \min\limits_{y \in Son(x)} \{ \max(f_{y,0},f_{y,1})-f_{y,0} \}\)

最后处理下环形 DP 的取或不取即可。

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define ull unsigned long long
#define sort stable_sort 
#define endl '\n'
struct node
{
    int nxt,to;
}e[2000010];
int head[2000010],vis[2000010],u[2000010],v[2000010],f[2000010][2],rt,cnt=0;
void add(int u,int v)
{
    cnt++;
    e[cnt].nxt=head[u];
    e[cnt].to=v;
    head[u]=cnt;
}
int dfs_huan(int x)
{
    vis[x]=1;
    return (vis[u[x]]==1)?x:dfs_huan(u[x]);
}
void dfs(int x)
{
    int minn=0x3f3f3f3f;
    vis[x]=1;
    f[x][0]=0;
    for(int i=head[x];i!=0;i=e[i].nxt)
    {
        if(e[i].to==rt)
        {
            f[e[i].to][1]=-0x3f3f3f3f;
        }
        else
        {
            dfs(e[i].to);
            f[x][0]+=max(f[e[i].to][0],f[e[i].to][1]);
            minn=min(minn,max(f[e[i].to][0],f[e[i].to][1])-f[e[i].to][0]);
        }
    }
    f[x][1]=1+f[x][0]-minn;
}
int main()
{
    int n,ans=0,maxx,i;
    cin>>n;
    for(i=1;i<=n;i++)
    {
        v[i]=i;
        cin>>u[i];
        add(u[i],v[i]);
    }
    for(i=1;i<=n;i++)
    {
        if(vis[i]==0)
        {
            rt=dfs_huan(i);
            dfs(rt);
            maxx=max(f[rt][0],f[rt][1]);
            rt=u[rt];
            dfs(rt);
            ans+=max(maxx,f[rt][1]);
        }
    }
    cout<<ans<<endl;
    return 0;
}
posted @ 2024-08-24 21:06  hzoi_Shadow  阅读(11)  评论(0编辑  收藏  举报
扩大
缩小