P4577 [FJOI2018] 领导集团问题

容易写出 \(\mathcal O(n^2)\)\(dp\):设 \(f_{u,i}\) 表示在 \(u\) 的子树内选数,最小的数是 \(i\) 能选最多的个数。转移:\(f_{u,\min(i,j)}=\max(f_{u,i}+f_{v,j})\)
考虑把 \(u\) 选上 \(f_{u,w_u}=1+max_{j\geq w_u}(f_{u,j})\)
使用线段树合并优化,\(merge\) 的时候维护一下 \(f_u\)\(f_v\) 的后缀 \(max\) 即可。
一些细节:\(merge\) 时如果两个节点中有一个是空的,那么需要打一个区间加的懒标记,但是懒标记下穿时不能新建子节点,因为这样会导致 \(merge\) 边下穿懒标记边遍历,让复杂度假掉。仔细观察后发现如果一个点的儿子是空的话,这个点的懒标记对空儿子是无效的。

代码

/*
Luogu P4577 [FJOI2018] 领导集团问题
2026-03-20
*/
#include<bits/stdc++.h>
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T&x){
        x=0;char c=getchar();bool f=0;
        while(!isdigit(c)) c=='-'?f=1:0,c=getchar();
        while(isdigit(c)) x=x*10+c-'0',c=getchar();
        f?x=-x:0;
    }
    template<typename T>
    inline void write(T x){
        if(x==0){putchar('0');return ;}
        x<0?x=-x,putchar('-'):0;short st[50],top=0;
        while(x) st[++top]=x%10,x/=10;
        while(top) putchar(st[top--]+'0');
    }
    inline void read(char&c){c=getchar();while(isspace(c)) c=getchar();}
    inline void write(char c){putchar(c);}
    inline void read(string&s){s.clear();char c;read(c);while(!isspace(c)&&~c) s+=c,c=getchar();}
    inline void write(string s){for(int i=0,len=s.size();i<len;i++) putchar(s[i]);}
    template<typename T>inline void write(T*x){while(*x) putchar(*(x++));}
    template<typename T,typename...T2> inline void read(T&x,T2&...y){read(x),read(y...);}
    template<typename T,typename...T2> inline void write(const T x,const T2...y){write(x),putchar(' '),write(y...),sizeof...(y)==1?putchar('\n'):0;}
}using namespace IO;
const int maxn=200010;
int n,w[maxn],p[maxn];
vector<int>e[maxn];
class Segment_Tree{
private:
    struct node{
        int ch[2],max,lan;
        node(){max=0;}
    }t[maxn*40];
    int rt[maxn],cnt;
    void add_tag(int u,int z){if(u) t[u].lan+=z,t[u].max+=z;}
    void up(int u){t[u].max=max({t[t[u].ch[0]].max,t[t[u].ch[1]].max});}
    void down(int u){
        add_tag(t[u].ch[0],t[u].lan),add_tag(t[u].ch[1],t[u].lan);
        t[u].lan=0;
    }
    void insert(int&u,int l,int r,int d,int z){
        if(l>d||r<d) return ;
        if(u==0) u=++cnt;
        if(l==r){t[u].max=max(t[u].max,z);return ;}
        down(u);
        int mid=l+r>>1;
        insert(t[u].ch[0],l,mid,d,z),insert(t[u].ch[1],mid+1,r,d,z);
        up(u);
    }
    void merge(int&u,int uu,int max1,int max2,int l,int r){
        if(!u&&!uu) return ;
        if(!u){u=uu;add_tag(u,max1);return ;}
        if(!uu){add_tag(u,max2);return ;}
        if(l==r){max1=max(max1,t[u].max),max2=max(max2,t[uu].max);t[u].max=max1+max2;return ;}
        int mid=l+r>>1;
        down(u),down(uu);
        int nmax1=max(max1,t[t[u].ch[1]].max);
        int nmax2=max(max2,t[t[uu].ch[1]].max);
        merge(t[u].ch[0],t[uu].ch[0],nmax1,nmax2,l,mid);
        merge(t[u].ch[1],t[uu].ch[1],max1,max2,mid+1,r);
        up(u);
    }
    int query(int u,int l,int r,int ll,int rr){
        if(u==0) return 0;
        if(l>rr||r<ll) return 0;
        if(ll<=l&&r<=rr) return t[u].max;
        down(u);
        int mid=l+r>>1;
        int ans=max(query(t[u].ch[0],l,mid,ll,rr),query(t[u].ch[1],mid+1,r,ll,rr));
        up(u);
        return ans;
    }
public:
    void insert(int id,int d,int z){insert(rt[id],1,n,d,z);}
    void merge(int id,int idd){merge(rt[id],rt[idd],0,0,1,n);}
    int query(int u){return t[rt[u]].max;}
    int query(int id,int l,int r){return query(rt[id],1,n,l,r);}
}t;
void dfs(int u){
    for(int v:e[u]) dfs(v);
    for(int v:e[u]) t.merge(u,v);
    int z=t.query(u,w[u],n)+1;
    t.insert(u,w[u],z);
}
signed main(){
    read(n);
    for(int i=1;i<=n;i++) read(w[i]),p[i]=w[i];
    sort(p+1,p+1+n);
    int cnt_p=unique(p+1,p+1+n)-p-1;
    for(int i=1;i<=n;i++) w[i]=lower_bound(p+1,p+1+cnt_p,w[i])-p;
    for(int i=2;i<=n;i++){
        int fa;read(fa);
        e[fa].push_back(i);
    }
    dfs(1);
    write(t.query(1));
    return 0;
}
posted @ 2026-03-20 22:04  Link-Cut_Trees  阅读(3)  评论(0)    收藏  举报