BZOJ 2809: [Apio2012]dispatching(左偏树)
http://www.lydsy.com/JudgeOnline/problem.php?id=2809
题意:
思路:
最简单的想法就是枚举管理者,在其子树中从薪水低的开始选起,但是每个节点都这样处理的话就会重复计算。比如说,现在有两棵子树y,z已经处理好了,然后有一个顶点x连接着这两棵子树,现在要求的是当x为管理者时的最大满意度,其实没必要再去遍历所有x的子节点,因为y、z已经遍历过了,x所选忍者的肯定在y和z所选的忍者当中,如果它们的薪水和>m,那么就剃去最大的,直到薪水和<m。这样需要处理最大值和子树的合并,可以用左偏树来处理。
#include<iostream> #include<cstdio> #include<vector> using namespace std; typedef long long ll; const int maxn = 100000+5; ll n,m; ll ans; int master; struct Heap { int l,r,dis,sz,root; ll salary,leading,sum; } t[maxn]; vector<int> g[maxn]; int merge(int x, int y) { if(x==0) return y; if(y==0) return x; if(t[y].salary>t[x].salary) swap(x,y); t[x].r = merge(t[x].r,y); t[x].sum = t[t[x].l].sum + t[t[x].r].sum + t[x].salary; t[x].sz = t[t[x].l].sz + t[t[x].r].sz + 1; if(t[t[x].l].dis < t[t[x].r].dis) swap(t[x].l,t[x].r); if(t[x].r == 0) t[x].dis = 0; else t[x].dis = t[t[x].r].dis + 1; return x; } int pop(int &x) { x = merge(t[x].l,t[x].r); } int dfs(int u) { for(int i=0;i<g[u].size();i++) { int v = g[u][i]; dfs(v); t[u].root = merge(t[u].root,t[v].root); while(t[t[u].root].sum>m) pop(t[u].root); } ans = max(ans, t[t[u].root].sz*t[u].leading); return 0; } int main() { //freopen("in.txt","r",stdin); scanf("%lld%lld",&n,&m); for(int i=1; i<=n; i++) { ll f,s,l; scanf("%lld%lld%lld",&f,&s,&l); if(f==0) master = i; t[i].l=t[i].r=t[i].dis = 0; t[i].salary = t[i].sum = s; t[i].leading = l; t[i].root = i; t[i].sz = 1; g[f].push_back(i); } ans = 0; dfs(master); printf("%lld\n",ans); return 0; }