APIO2012 派遣 解題報告

2021-06-28 03:22:52 字數 3110 閱讀 5604

【問題描述】 

在乙個忍者的幫派裡,一些忍者們被選中派遣給顧客,然後依據自己的工作獲取報償。

在這個幫派裡,有一名忍者被稱之為master。除了master以外,每名忍者都有且僅有乙個上級。為保密,同時增強忍者們的領導力,所有與他們工作相關的指令總是由上級傳送給他的直接下屬,而不允許通過其他的方式傳送。

現在你要招募一批忍者,並把它們派遣給顧客。你需要為每個被派遣的忍者支付一定的薪水,同時使得支付的薪水總額不超過你的預算。另外,為了傳送指令,你需要選擇一名忍者作為管理者,要求這個管理者可以向所有被派遣的忍者傳送指令,在傳送指令時,任何忍者(不管是否被派遣)都可以作為訊息的傳遞人。管理者自己可以被派遣,也可以不被派遣。當然,如果管理者沒有被排遣,你就不需要支付管理者的薪水。 

你的目標是在預算內使顧客的滿意度最大。這裡定義顧客的滿意度為派遣的忍者總數乘以管理者的領導力水平,其中每個忍者的領導力水平也是一定的。 

寫乙個程式,給定每乙個忍者i的上級bi,薪水ci,領導力li,以及支付給忍者們的薪水總預算m,輸出在預算內滿足上述要求時顧客滿意度的最大值。 

【資料範圍】 

1 ≤ n ≤ 100,000  忍者的個數; 

1 ≤ m ≤ 1,000,000,000  薪水總預算; 

0 ≤ bi < i  忍者的上級的編號; 

1 ≤ ci ≤ m  忍者的薪水; 

1 ≤ li ≤ 1,000,000,000  忍者的領導力水平。 

對於30%的資料,n ≤ 3000。 

【輸入格式】 

從標準輸入讀入資料。 

第一行包含兩個整數n和m,其中n表示忍者的個數,m表示薪水的總預算。 

接下來n行描述忍者們的上級、薪水以及領導力。其中的第i行包含三個整數bi , ci , li 分別表示第i個忍者的上級,薪水以及領導力。master滿足bi = 0,並且每乙個忍者的老闆的編號一定小於自己的編號 bi < i。 

【輸出格式】 

輸出到標準輸出。 

輸出乙個數,表示在預算內顧客的滿意度的最大值。

【樣例輸入】 

5 4 

0 3 3 

1 3 5 

2 2 2 

1 2 4 

2 3 1 

【樣例輸出】 

6 【樣例說明】 

如果我們選擇編號為1的忍者作為管理者並且派遣第三個和第四個忍者,薪水總和為4,沒有超過總預算4。因為派遣了2個忍者並且管理者的領導力為3,使用者的滿意度為2 × 3 = 6,是可以得到的使用者滿意度的最大值。

a的第一道左偏樹的題,花了好久。

貪心是顯然的,如果有工資更小的忍者沒有被選上,顯然選上他更優;但是,做起來的時候,應該是反著做的!①只需要維護乙個大根堆就可以了。如果它大於m了,就不斷把大根彈出即可。正難則反的思想!一定要注意加強運用。

結果我很sb地維護了兩個堆。。還維護了每個元素在小根堆中的對映;然後我需要在小根堆中刪除非根節點。。然後我就呵呵了。。

寫了很久才寫出來,發現②刪除非根節點時需要維護父指標,並且更改merge函式,在merge函式的開首先維護a、b的父指標;先刪除節點父親指向其的子指標,合併所刪除節點的左右子樹,然後將其與原樹合併。。。也算有了些收穫吧;至少研究出怎麼刪除非根節點了,並沒有看上去那麼簡單。

③設定空節點,這玩意兒一定要有,太管事了!一開始由於沒有空節點,蛋疼了好久還是炸;結果一改就對了。

④還有乙個就是型別!我起初竟然用%d輸出了long long!

但是。。說了這麼多,首要反思的還應該是思路,根本就不需要小根堆!

這是原先的**:

#includeusing namespace std;

#include#include#include#include#includechar * ptr=(char *)malloc(5000000);

inline void in(int &x)

#define maxn 100001

#includevectorson[maxn];

int sum[maxn],num[maxn],b[maxn],c[maxn],l[maxn];

struct ls*null=new ls((ls)),*root[maxn][2],*point[maxn];

ls * merge(ls * a,ls * b,ls * f,bool flag)

int main()

long long ans=0;

ls * tmp;

for(i=n;i;--i));

root[i][1]=new ls((ls));

sum[i]=c[i],num[i]=1;

point[i]=root[i][0];

} else

for(j=son[i].size();j--;)

root[i][1]=merge(root[i][1]->c[0],root[i][1]->c[1],null,1);

}} ans=max(ans,(long long)l[i]*num[i]);

} cout<

#includeusing namespace std;

#include#include#include#include#includechar * ptr=(char *)malloc(5000000);

inline void in(int &x)

#define maxn 100001

#includevectorson[maxn];

int sum[maxn],num[maxn],b[maxn],c[maxn],l[maxn];

struct ls*null=new ls((ls)),*root[maxn];

ls * merge(ls * a,ls * b)

int main()

long long ans=0;

for(i=n;i;--i));

sum[i]=c[i],num[i]=1;

} else root[i]=null;

for(j=son[i].size();j--;)

} ans=max(ans,(long long)l[i]*num[i]);

} cout<

APIO2012 派遣 題解

這題還是非常顯然的 題意大概是隨便選乙個點x,樹上每點有點權 v x 和乙個代價 c x 你有乙個值m,設,我們用m可以最多大於y x 個x的子樹中代價的和,求出所有點的y x v x 的值,其中最大值就是答案。非常顯然用線段樹合併 還有可並堆的做法,不怎麼會會,下次學 include includ...

APIO2012 派遣 左偏樹

題面 考慮列舉每個節點作為管理者,計算所獲得的滿意程度以更新答案。對於每個節點的計算,貪心,維護乙個大根堆,每次彈出薪水最大的人。這裡注意,一旦乙個人被彈出,那麼不再可能出現在其祖先們的最優解裡 廢話 所以使用可並堆左偏樹優化複雜度。include include define maxn 10001...

左偏樹 APIO2012 派遣

題意可真的是有毒 第一眼樹形揹包可做?反正我沒用樹形揹包打過,邊上巨佬打的揹包似乎沒拿分 後來發現可以貪心搞,我們先把乙個節點所有的兒子都取進去,之後不行的話再從大的開始拿走就好了 問題就變成了了如何快速維護各個節點子樹中的最大值,優先佇列就好了!關鍵是還要資瓷合併,pb ds庫就好了,手打左偏樹就...