1. 程式人生 > >BZOJ2809:[APIO2012]dispatching

BZOJ2809:[APIO2012]dispatching

淺談左偏樹:https://www.cnblogs.com/AKMer/p/10246635.html

題目傳送門:https://lydsy.com/JudgeOnline/problem.php?id=2809

對於每個子樹維護一棵滿足大根堆性質的左偏樹,可以通過當前節點和子樹的左偏樹合併得來,如果總權值超過\(m\)就不斷\(pop\)堆頂,每個點只會被\(pop\)一次。滿足大根堆性質是貪心,權值小的忍者越多我就可以僱傭越多人。然後用這顆子樹的根的領導能力乘以左偏樹結點個數來更新答案就行了。

時間複雜度:\(O(nlogn)\)

空間複雜度:\(O(n)\)

程式碼如下:

#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;

const int maxn=1e5+5;

ll ans;
int n,m,tot;
int c[maxn],l[maxn];
int now[maxn],pre[maxn],to[maxn];
int son[maxn][2],dist[maxn],siz[maxn],val[maxn];

int read() {
    int x=0,f=1;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
    return x*f;
}

void add(int a,int b) {
    pre[++tot]=now[a];
    now[a]=tot,to[tot]=b;
}

void update(int u) {
    siz[u]=siz[son[u][0]]+1+siz[son[u][1]];
    val[u]=val[son[u][0]]+c[u]+val[son[u][1]];
    dist[u]=dist[son[u][1]]+1;
}

int merge(int a,int b) {
    if(!a||!b)return a+b;
    if(c[a]<c[b])swap(a,b);
    son[a][1]=merge(son[a][1],b);
    if(dist[son[a][1]]>dist[son[a][0]])
        swap(son[a][1],son[a][0]);
    update(a);return a;
}

int pop(int u) {
    int tmp=merge(son[u][1],son[u][0]);
    son[u][0]=son[u][1];
    return tmp;
}

int dfs(int fa,int u) {
    int tmp=u;siz[u]=1,val[u]=c[u];
    for(int p=now[u],v=to[p];p;p=pre[p],v=to[p]) {
        tmp=merge(tmp,dfs(u,v));
        while(val[tmp]>m)tmp=pop(tmp);
    }
    ans=max(ans,1ll*siz[tmp]*l[u]);
    return tmp;
}

int main() {
    n=read(),m=read(),dist[0]=-1;
    for(int i=1;i<=n;i++) {
        int FA=read();add(FA,i);
        c[i]=read(),l[i]=read();
    }
    dfs(0,1);
    printf("%lld\n",ans);
    return 0;
}