1. 程式人生 > >[BZOJ1040/Luogu2607][ZJOI2008]騎士

[BZOJ1040/Luogu2607][ZJOI2008]騎士

mat getchar() 最大 tin 推薦 強烈 時間 getchar git

題目鏈接:

BZOJ1040

Luogu2607

第一眼看題目:最大獨立點集?秒了!

看數據範圍:\(1e6\)自閉了

貪心?這種題顯然不能貪心吧。。

把題目轉成圖:每個人有一條出邊連向他人。

那麽就是個基環樹森林。。

然後再看題目模型。。。woc,上司的舞會?

那麽這就是基環樹\(DP\)了。

對於每一個環,選一條環上的邊\((x,y)\)斷開。

\(f_{x,0/1}\)表示以\(x\)為根的子樹內,不選/選\(x\)的最大戰鬥力。

答案就是\(\max\{f_{x,0},f_{y,0}\}\)(總有一個點不能選)。

這裏我用並查集來找環,雖然慢了一些(\(Luogu\)最後一點\(973ms\)),但是好寫。強烈推薦

什麽,不會樹形\(DP\)?

轉移式:

\[f_{x,0}=\sum\max\{f_{y,0},f_{y,1}\}\]

\[f_{x,1}=\sum f_{y,0}\]

時間復雜度 \(O(n\alpha(n))\)

#include <cstdio>
#include <cctype>
typedef long long ll;

inline ll Max(ll a,ll b){return a>b?a:b;}
inline int Getint()
{
    register int x=0,c;
    while(!isdigit(c=getchar()));
    for(;isdigit(c);c=getchar())x=x*10+(c^48);
    return x;
}

int n,Att[1000005],Fa[1000005],Ls[1000005],Rs[1000005],Cs;
int Head[1000005],Next[2000005],To[2000005],En;
ll f[1000005][2],Ans;

inline void Add(int x,int y)
{Next[++En]=Head[x],To[Head[x]=En]=y;}
int Get(int x){return x==Fa[x]?x:Fa[x]=Get(Fa[x]);}

void DP(int x,int Pre)
{
    f[x][0]=0,f[x][1]=Att[x];
    for(int i=Head[x],y;i;i=Next[i])
        if((y=To[i])!=Pre)
        {
            DP(y,x);
            f[x][0]+=Max(f[y][0],f[y][1]);
            f[x][1]+=f[y][0];
        }
}

int main()
{
    n=Getint();
    for(int i=1;i<=n;++i)Fa[i]=i;
    for(int i=1,x;i<=n;++i)
    {
        Att[i]=Getint(),x=Getint();
        int Fx=Get(i),Fy=Get(x);
        if(Fx==Fy)Ls[++Cs]=i,Rs[Cs]=x;
        else Add(i,x),Add(x,i),Fa[Fx]=Fy;
    }
    for(int i=1;i<=Cs;++i)
    {
        DP(Ls[i],0);
        ll v=f[Ls[i]][0];
        DP(Rs[i],0);
        Ans+=Max(v,f[Rs[i]][0]);
    }
    printf("%lld\n",Ans);
    return 0;
}

[BZOJ1040/Luogu2607][ZJOI2008]騎士