[Apio2012]dispatching 主席树做法
bzoj 2809: [Apio2012]dispatching
http://www.lydsy.com/JudgeOnline/problem.php?id=2809
Description
在一個忍者的幫派里,一些忍者們被選中派遣給顧客,然后依據自己的工作獲取報償。在這個幫派里,有一名忍者被稱之為?Master。除了?Master以外,每名忍者都有且僅有一個上級。為保密,同時增強忍者們的領導力,所有與他們工作相關的指令總是由上級發送給他的直接下屬,而不允許通過其他的方式發送。現在你要招募一批忍者,并把它們派遣給顧客。你需要為每個被派遣的忍者 支付一定的薪水,同時使得支付的薪水總額不超過你的預算。另外,為了發送指令,你需要選擇一名忍者作為管理者,要求這個管理者可以向所有被派遣的忍者 發送指令,在發送指令時,任何忍者(不管是否被派遣)都可以作為消息的傳遞 人。管理者自己可以被派遣,也可以不被派遣。當然,如果管理者沒有被排遣,就不需要支付管理者的薪水。你的目標是在預算內使顧客的滿意度最大。這里定義顧客的滿意度為派遣的忍者總數乘以管理者的領導力水平,其中每個忍者的領導力水平也是一定的。寫一個程序,給定每一個忍者?i的上級?Bi,薪水Ci,領導力L i,以及支付給忍者們的薪水總預算?M,輸出在預算內滿足上述要求時顧客滿意度的最大值。 1??≤N?≤ 100,000?忍者的個數; 1??≤M?≤ 1,000,000,000?薪水總預算;? 0??≤Bi < i??忍者的上級的編號; 1??≤Ci ≤ M?????????????????????忍者的薪水; 1??≤Li ≤ 1,000,000,000?????????????忍者的領導力水平。Input
從標準輸入讀入數據。 第一行包含兩個整數?N和?M,其中?N表示忍者的個數,M表示薪水的總預算。 接下來?N行描述忍者們的上級、薪水以及領導力。其中的第?i?行包含三個整?Bi , C i , L i分別表示第i個忍者的上級,薪水以及領導力。Master滿足B i = 0,并且每一個忍者的老板的編號一定小于自己的編號?Bi < i。Output
輸出一個數,表示在預算內顧客的滿意度的最大值。Sample Input
5 40 3 3
1 3 5
2 2 2
1 2 4
2 3 1
Sample Output
6HINT
如果我們選擇編號為 1的忍者作為管理者并且派遣第三個和第四個忍者,薪水總和為 4,沒有超過總預算。
因為派遣了2 個忍者并且管理者的領導力為 3,用戶的滿意度為 6 ?,是可以得到的用戶滿意度的最大值。
題目剖析:
ans=領導力*個數
領導力:枚舉每個忍者
個數:當然是需要薪水少的先派遣啦
所以,節點的領導力為a,在以這個節點為根的子樹中(包括這個節點),從小到大選盡可能多的節點s,滿足節點的薪水和<=總預算,最大化a*s
這道題還可以用左偏樹來做,但蒟蒻一枚只會主席樹 ,所以這里只寫主席樹做法
推薦黃學長的博客,維護子樹大根堆,用可并堆將時間復雜度降為nlongn ?http://hzwer.com/5682.html
數據結構:主席樹
輔助:dfs序(開始沒想到)
以忍者的dfs序為下標,薪水為區間建立主席樹
首先說說dfs序
1、為什么需要dfs序?
因為派遣忍者的范圍是以這個忍者為根的子樹。
假設當前節點為i,遍歷到時令 in[i]=a, 表示i是第a個被遍歷。以i為節點的子樹遍歷完后,令out[i]=b,b為當前所有已遍歷節點總數
那么這個節點可以派遣的范圍是dfs序[a,b](包括a,b)之間的忍者
所dfs序的作用是鎖定查找區間
2、如何構造dfs序?(自己也沒想到)
令t表示當前已遍歷節點總數,每遍歷一個節點t++,設當前節點為i,遍歷到i是in[i]=t,i退出時out[i]=t
在構造dfs序時,順便記錄下dfs序為i的忍者的薪水、領導力,然后按照dfs序在主席樹中插入節點
最后是查找
枚舉每個忍者,查找他的派遣范圍最多能派遣多少個忍者
如果做過[CQOI2015]任務查詢系統,會知道那道題在查詢查到葉子節點時,對答案的貢獻是sum[l]/count[l]*k
本題查詢的是數量,到葉子節點時有沒有坑呢?
有。葉子節點對答案有貢獻的忍者數量應該是 min(薪水預算/派遣這個節點的忍者所需薪水,這個節點的忍者個數)
這里的節點是主席樹中以薪水為區間建立的節點
還有就是要注意答案用long long
#include<cstdio> #include<algorithm> #define N 100001 using namespace std; int n,m,money[N],lead[N],front[N],next[N]; int hash[N],cnt_money; int in[N],out[N],t; int root[N],tot,lc[N*20],rc[N*20],cnt[N*20]; long long sum[N*20],ans,dispatch; struct node {int mon,lea; }e[N]; inline void add(int u,int v) {next[v]=front[u];front[u]=v; } inline int dfs(int r) {in[r]=++t;e[t].lea=lead[r];e[t].mon=hash[r];if(front[r])for(int i=front[r];i;i=next[i])dfs(i);out[r]=t; } void discrete() {sort(money+1,money+n+1); cnt_money=unique(money+1,money+n+1)-(money+1);for(int i=1;i<=n;i++) hash[i]=lower_bound(money+1,money+cnt_money+1,hash[i])-money; } inline void insert(int pre,int & now,int l,int r,int w) {sum[now=++tot]=sum[pre]+money[w];cnt[now]=cnt[pre]+1;if(l==r) return;int mid=l+r>>1;if(w<=mid){rc[now]=rc[pre];insert(lc[pre],lc[now],l,mid,w);}else{lc[now]=lc[pre];insert(rc[pre],rc[now],mid+1,r,w);} } inline int query(int x,int y,int l,int r,long long k) {if(l==r) return min(k/(long long)money[l],(long long)(cnt[y]-cnt[x]));int mid=l+r>>1;long long tmp=sum[lc[y]]-sum[lc[x]];if(k<=tmp) return query(lc[x],lc[y],l,mid,k);else return cnt[lc[y]]-cnt[lc[x]]+query(rc[x],rc[y],mid+1,r,k-tmp); } int main() {scanf("%d%lld",&n,&m);int b,c,l;for(int i=1;i<=n;i++){scanf("%d%d%d",&b,&money[i],&lead[i]);hash[i]=money[i];add(b,i);}discrete();dfs(front[0]);for(int i=1;i<=n;i++) insert(root[i-1],root[i],1,cnt_money,e[i].mon); for(int i=1;i<=n;i++){int l=in[i],r=out[i];//l:節點i的dfs序,r節點i可以派遣的最靠后的忍者的dfs序 dispatch=query(root[l-1],root[r],1,cnt_money,m);ans=max(ans,(long long)lead[i]*dispatch);}printf("%lld",ans); }這道題做的時候
1、沒有想到用dfs序鎖定查找區間
2、ans=max(忍者的領導力*派遣忍者個數),沒有想到可以枚舉每個忍者,這樣就解決了領導力的問題。對答案的分解能力欠缺
3、答案沒用long long,開始做的時候還想著答案用long long,想先寫出來過了樣例后再改,結果忘了
4、查詢時,如果當前區間為[x,y],要到節點的右孩子去查詢,返回的答案應是區間左右端點左孩子的cnt差,而不是y的cnt。這里與任務查詢系統混了,因為任務查詢系統查詢的區間是[0,y],0可以省略,所以可以直接用y的cnt、sum
?
轉載于:https://www.cnblogs.com/TheRoadToTheGold/p/6368387.html
總結
以上是生活随笔為你收集整理的[Apio2012]dispatching 主席树做法的全部內容,希望文章能夠幫你解決所遇到的問題。