[洛谷P1552] [APIO2012]派遣(左偏樹)
阿新 • • 發佈:2018-03-24
一個 LG style getch 大根堆 math lld algorithm ron 這道題是我做的左偏樹的入門題,奈何還是看了zsy大佬的題解才能過,唉,我太弱了。
都理解了題目了,就很清楚了,我們在dfs過程中記錄一下集合元素,並把此節點以下所有的子樹全部合並入一個堆裏(dfs過程中已經處理了,每個堆都是最優堆),那麽開始彈元素,維護大根堆,一個個彈出最大值,直到剛好符合要求,此時答案就是堆中剩余元素的數量×當前節點的管理能力,取max。完啦!
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 #definelst 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]派遣(左偏樹)