1. 程式人生 > >【BZOJ5469】[FJOI2018]領導集團問題(動態規劃,線段樹合並)

【BZOJ5469】[FJOI2018]領導集團問題(動態規劃,線段樹合並)

狀態 space 位置 ret max flag 得到 modify 線段樹合並

【BZOJ5469】[FJOI2018]領導集團問題(動態規劃,線段樹合並)

 題面

BZOJ
洛谷

題解

題目就是讓你在樹上找一個最大的點集,使得兩個點如果存在祖先關系,那麽就要滿足祖先的權值要小於等於兒子的權值。
首先離散權值。
考慮一個暴力\(dp\),設\(f[i][j]\)表示以\(i\)為根,子樹中被選擇的最小值為\(j\)時能夠被選出的最大點樹。然後xjb轉移一下就寫出了一個\(O(n^3)\)的優秀做法。
然後把狀態從恰好變成至少,然後就得到了一個\(O(n^2)\)的做法。
考慮\(O(n^2)\)\(dp\),本質上就是在維護一個後綴的最大值。
不難發現後綴最大值一定是不降的。

考慮對於後綴進行差分,每次相當於現在\(w[i]\)位置加一,然後往前更新一段,直到下一個差分數組上有\(1\)的位置。
那麽用線段樹合並就可以解決這個問題,給\(w[i]\)位置加一,然後線段樹上二分找到上一個為\(1\)的位置然後把它減一就好了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAX 200200
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
struct Line{int v,next;}e[MAX<<1];
int h[MAX],cnt=1;
inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;}
int n,ans,w[MAX],S[MAX],tot,rt[MAX];
int ls[MAX*18],rs[MAX*18],t[MAX*18],node;
void Modify(int &x,int l,int r,int p)
{
    if(!x)x=++node;t[x]+=1;if(l==r)return;
    int mid=(l+r)>>1;
    if(p<=mid)Modify(ls[x],l,mid,p);
    else Modify(rs[x],mid+1,r,p);
}
bool Flag;
void Calc(int x)
{
    if(!x)return;t[x]-=1;
    if(t[rs[x]])Calc(rs[x]);
    else Calc(ls[x]);
}
void Minus(int x,int l,int r,int p)
{
    if(l==r)return;int mid=(l+r)>>1;
    if(p<=mid)Minus(ls[x],l,mid,p);
    else
    {
        Minus(rs[x],mid+1,r,p);
        if(!Flag&&t[ls[x]])Flag=true,Calc(ls[x]);
    }
    if(Flag)t[x]-=1;
}
void Merge(int &x,int &y)
{
    if(!x||!y){x|=y;return;}t[x]+=t[y];
    Merge(ls[x],ls[y]);Merge(rs[x],rs[y]);
}
void dfs(int u)
{
    for(int i=h[u];i;i=e[i].next)
        dfs(e[i].v),Merge(rt[u],rt[e[i].v]);
    Modify(rt[u],1,tot,w[u]);
    Flag=false;Minus(rt[u],1,tot,w[u]);
}
int main()
{
    n=read();
    for(int i=1;i<=n;++i)S[++tot]=w[i]=read();
    for(int i=2;i<=n;++i)Add(read(),i);
    S[++tot]=1e9+1;sort(&S[1],&S[tot+1]);tot=unique(&S[1],&S[tot+1])-S-1;
    for(int i=1;i<=n;++i)w[i]=lower_bound(&S[1],&S[tot+1],w[i])-S;
    dfs(1);printf("%d\n",t[rt[1]]);
    return 0;
}

【BZOJ5469】[FJOI2018]領導集團問題(動態規劃,線段樹合並)