BZOJ-3037/2068: [Poi2004]SZP

[文章目录]

Description

给你一个含有n个元素的集合,每个元素都可以限制另一种元素,选出最大子集使得对于该子集中每个元素都被其补集中另一个元素限制。n<=10^6

基环树DP。被限制元素为父亲,限制元素为儿子。
f[i]表示选i,g[i]表示不选i转移即可。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define N 1001000 
const int inf=0x3f3f3f3f;
inline char nc()
{
    static char buf[100000],*p1,*p2;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &x)
{
    x=0; char ch=nc();
    while(!isdigit(ch)) ch=nc();
    while(isdigit(ch)) x=x*10+ch-'0',ch=nc();
}
int n,fa[N],za[N],zb[N],tot;
int head[N],to[N],nxt[N],cnt;
void add(int x,int y) {to[++cnt]=y; nxt[cnt]=head[x]; head[x]=cnt;}
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
int f[N],g[N],lim;
void dfs(int x)
{
    g[x]=0; int t=-inf;
    for(int i=head[x];i;i=nxt[i])
    {
        if(to[i]!=lim) dfs(to[i]);
        g[x]+=max(f[to[i]],g[to[i]]);
        t=max(t,g[to[i]]-max(f[to[i]],g[to[i]]));
    }
    f[x]=g[x]+t+1;
}
int main()
{
    read(n);
    for(int i=1;i<=n;++i) fa[i]=i;
    int x;
    for(int i=1;i<=n;++i)
    {
        read(x);
        if(find(x)==find(i)) za[++tot]=i,zb[tot]=x;
        else fa[fa[x]]=fa[i],add(x,i);
    }
    int ans=0,tmp;
    for(int i=1;i<=tot;++i)
    {
        lim=0; dfs(za[i]); tmp=max(f[za[i]],g[za[i]]);
        f[zb[i]]=g[zb[i]]+1; g[zb[i]]=-inf; lim=zb[i]; dfs(za[i]);
        ans+=max(tmp,g[za[i]]);
    }
    printf("%d",ans);
    return 0;
}

发表评论

邮箱地址不会被公开。 必填项已用*标注