• BZOJ 3037. 创世纪


    传送门

    一眼基环树森林上面搞搞 $dp$

    本来如果是颗树,直接设 $f[x][0/1]$ 表示节点 $x$ 不选/选 时子树的最大价值

    因为有环,所以设 $f[x][0/1/2]$ 表示节点 $x$ 不选/选且有非环上儿子控制/选且没非环上儿子控制 时非环上子树的最大价值

    对环上每个节点往子树内跑一遍 $dp$,然后在环上分类讨论一波,断环为链,对于环上第一个节点 $x$,分成 $3$ 种情况

    $1.$ 不选,$2.$ 选且非环儿子控制,$3.$ 选且强制环儿子控制,三种情况取个 $max$ 即可

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    inline int read()
    {
        int x=0,f=1; char ch=getchar();
        while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
        while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return x*f;
    }
    const int N=2e6+7,INF=1e9+7;
    int fir[N],from[N<<1],to[N<<1],cntt;
    inline void add(int a,int b) { from[++cntt]=fir[a]; fir[a]=cntt; to[cntt]=b; }
    int n,fa[N],f[N][3],g[N][2],ans;
    bool ring[N],vis[N];
    int st[N],Top;
    void dfs(int x)
    {
        int sum=0,mi=INF; vis[x]=1;
        for(int i=fir[x];i;i=from[i])
        {
            int &v=to[i]; if(v==fa[x]||ring[v]) continue;
            dfs(v); sum+=max(f[v][0],f[v][1]);
            if(f[v][0]>=f[v][1]) mi=0;
            else mi=min(mi,f[v][1]-f[v][0]);
        }
        f[x][0]=sum; f[x][1]=sum-mi+1; f[x][2]=sum+1;
    }
    void DP()
    {
        for(int i=2;i<=Top;i++)
        {
            g[i][0]=f[st[i]][0]+max(g[i-1][0],g[i-1][1]);
            g[i][1]=max( f[st[i]][1]+max(g[i-1][0],g[i-1][1]) , f[st[i]][2]+g[i-1][0] );
        }
    }
    int solve(int x)
    {
        int res=0,t=x; Top=0;
        while(!vis[t]) vis[t]=1,t=fa[t];
        while(!ring[t]) ring[t]=1,st[++Top]=t,t=fa[t];
        for(int i=1;i<=Top;i++) dfs(st[i]);
        g[1][0]=f[st[1]][0]; g[1][1]=-INF; DP();
        res=max(g[Top][0],g[Top][1]);
        g[1][0]=-INF; g[1][1]=f[st[1]][1]; DP();
        res=max(res,max(g[Top][0],g[Top][1]));
        g[1][0]=-INF; g[1][1]=f[st[1]][2]; DP();
        res=max(res,g[Top][0]);
        return res;
    }
    int main()
    {
        n=read();
        for(int i=1;i<=n;i++) fa[i]=read(),add(fa[i],i);
        for(int i=1;i<=n;i++)
            if(!vis[i]) ans+=solve(i);
        printf("%d
    ",ans);
        return 0;
    }
  • 相关阅读:
    Minecraft 1.12.2/1.14.4 Mod开发笔记——搭建环境
    Minecraft 1.12.2 Mod开发笔记
    浅谈莫比乌斯反演
    卡迈克尔数
    一些可能会有用的东西(持续更新)
    emacs配置
    CSPS 2020游记
    浅谈KMP
    Atcoder AGC052
    乌班图操作指令(持续更新)
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/11495285.html
Copyright © 2020-2023  润新知