1. 程式人生 > >P1522 派遣 左偏樹

P1522 派遣 左偏樹

add get 水平 算法 最大值 oid urn esp stream

左偏樹就是一個應該用堆維護的區間,然後需要進行合並操作而發明的算法,其實這個算法沒什麽難的,和樹剖有點像,維護幾個數值,然後遞歸回來的時候就可以修改。

題幹:

題目背景

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

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

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

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

寫一個程序,給定每一個忍者i的上級Bi,薪水Ci,領導力Li,以及支付給忍者們的薪水總預算M,輸出在預算內滿足上述要求時顧客滿意度的最大值。
輸入輸出格式
輸入格式:

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

接下來N行描述忍者們的上級、薪水以及領導力。其中的第i行包含三個整數Bi,Ci,Li分別表示第i個忍者的上級,薪水以及領導力。Master滿足Bi
=0,並且每一個忍者的老板的編號一定小於自己的編號Bi<i。 輸出格式: 輸出一個數,表示在預算內顧客的滿意度的最大值。 輸入輸出樣例 輸入樣例#1: 復制 5 4 0 3 3 1 3 5 2 2 2 1 2 4 2 3 1 輸出樣例#1: 復制 6 說明 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

代碼:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<ctime>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
#define duke(i,a,n) for(int i = a;i <= n;i++)
#define lv(i,a,n) for(int i = a;i >= n;i--)
#define
clean(a) memset(a,0,sizeof(a)) const int INF = 1 << 30; typedef long long ll; typedef double db; template <class T> void read(T &x) { char c; bool op = 0; while(c = getchar(), c < 0 || c > 9) if(c == -) op = 1; x = c - 0; while(c = getchar(), c >= 0 && c <= 9) x = x * 10 + c - 0; if(op) x = -x; } template <class T> void write(T x) { if(x < 0) putchar(-), x = -x; if(x >= 10) write(x / 10); putchar(0 + x % 10); } int n,m,len = 0,lst[100005]; int c[100005],l[100004]; struct node { int l,r,nxt; }a[100005]; void add(int x,int y) { a[++len].l = x; a[len].r = y; a[len].nxt = lst[x]; lst[x] = len; } ll ans = 0; ll sum[100005],dis[100005]; int root[100005],ls[100005],rs[100005],siz[100005],v[100005]; int merge(int x,int y) { if(!x || !y) return x | y; if(v[x] < v[y]) swap(x,y); rs[x] = merge(rs[x],y); if(dis[ls[x]] < dis[rs[x]]) swap(ls[x],rs[x]); dis[x] = dis[rs[x]] + 1; siz[x] = siz[ls[x]] + siz[rs[x]] + 1; sum[x] = sum[ls[x]] + sum[rs[x]] + v[x]; return x; } void newnode(int x) { sum[x] = v[x] = c[x]; siz[x] = 1;root[x] = x; } int del(int x) { return merge(ls[x],rs[x]); } void dfs(int x) { newnode(x); for(int k = lst[x];k;k = a[k].nxt) { int y = a[k].r; dfs(y); root[x] = merge(root[x],root[y]); } while(sum[root[x]] > m && siz[root[x]]) root[x] = del(root[x]); ans = max(ans,(ll)siz[root[x]] * (ll)l[x]); } int main() { read(n);read(m); duke(i,1,n) { int x; read(x);read(c[i]);read(l[i]); if(x) add(x,i); } dfs(1); printf("%lld\n",ans); return 0; }

P1522 派遣 左偏樹