1. 程式人生 > >[洛谷P1552] [APIO2012]派遣(左偏樹)

[洛谷P1552] [APIO2012]派遣(左偏樹)

一個 LG style getch 大根堆 math lld algorithm ron

這道題是我做的左偏樹的入門題,奈何還是看了zsy大佬的題解才能過,唉,我太弱了。

Part 1 理解題目

很顯然,通過管理關系的不斷連邊,最後連出來的肯定是一棵樹,那麽不難得出,當一個忍者作為管理者時,最優解一定是去除掉所有的較大工資的忍者,剩下的忍者符合費用要求時,答案是管理者的管理能力×剩下的忍者數量。並且我們可以推出,當一棵子數中的一棵小子樹中去掉了一個忍者,那麽那個忍者一定不會對當前的子樹有答案貢獻。

Part 2 解題思想

都理解了題目了,就很清楚了,我們在dfs過程中記錄一下集合元素,並把此節點以下所有的子樹全部合並入一個堆裏(dfs過程中已經處理了,每個堆都是最優堆),那麽開始彈元素,維護大根堆,一個個彈出最大值,直到剛好符合要求,此時答案就是堆中剩余元素的數量×當前節點的管理能力,取max。完啦!

Part 3 code

    #include<iostream>  
    #include<cstdlib>  
    #include<cstdio>  
    #include<cmath>  
    #include<cstring>  
    #include<iomanip>  
    #include<algorithm>  
    #include<ctime>  
    #include<queue>  
    #define rg register  
    #define
lst long long using namespace std; #define ll long long const int N = 100005; struct edge{int to,next;}a[N]; int head[N],cnt; int n,Master,ls[N],rs[N],dis[N]; ll m,C[N],L[N],sum[N],sz[N],ans; ll gi() { ll x=0,w=1;char ch=getchar();
while ((ch<0||ch>9)&&ch!=-) ch=getchar(); if (ch==-) w=0,ch=getchar(); while (ch>=0&&ch<=9) x=(x<<3)+(x<<1)+ch-0,ch=getchar(); return w?x:-x; } void Link(int u,int v) { a[++cnt]=(edge){v,head[u]}; head[u]=cnt; } int Merge(int A,int B) { if (!A||!B) return A+B; if (C[A]<C[B]) swap(A,B); rs[A]=Merge(rs[A],B); if (dis[ls[A]]<dis[rs[A]]) swap(ls[A],rs[A]); dis[A]=dis[rs[A]]+1; return A; } int Delete(int A) { return Merge(ls[A],rs[A]); } int dfs(int u) { int A=u,B; sum[u]=C[u];sz[u]=1; for (int e=head[u];e;e=a[e].next) { int v=a[e].to; B=dfs(v); A=Merge(A,B); sum[u]+=sum[v];sz[u]+=sz[v]; } while (sum[u]>m) { sum[u]-=C[A];sz[u]--; A=Delete(A); } ans=max(ans,L[u]*sz[u]); return A; } int main() { n=gi();m=gi(); for (int i=1;i<=n;i++) { int u=gi(); if (!u) Master=i; else Link(u,i); C[i]=gi();L[i]=gi(); } dfs(Master); printf("%lld",ans); return 0; }

[洛谷P1552] [APIO2012]派遣(左偏樹)