[APIO2012]派遣
[APIO2012]派遣(左偏树,tag)
题目链接
Solution:
题目需要我们求出每个节点当领导时候,子树在一定预算内能取到的最多点,假设此时在i节点,显然需要知道左右子树的最大不超过m的取法,然后再筛选一下,这个就可以用左偏树去做了,左偏树不会的同学呢出门右拐左偏树模板走一手。
然后这题维护左偏树的子树和和子树大小就行。
可以发现合并的时候是单纯的子树和相加,子树大小相加,所以直接在merge之后,把两个根的信息合并即可,我们不需要知道子树内节点的信息,删除接电的时候直接把跟sum减去即可,但是做着做着发现左偏树貌似可以下放tag,并不影响,所以博主的代码是在merge的时候把子树信息全求出来了,这个具体选哪个方法这里没有啥区别
代码:
#include<bits/stdc++.h>
#define ll long long
#define R register
#define ls t[x].ch[0]
#define rs t[x].ch[1]
using namespace std;
template<class T>
void rea(T &x)
{
char ch=getchar();int f(0);x = 0;
while(!isdigit(ch)) {f|=ch=='-';ch=getchar();}
while(isdigit(ch)) {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
x = f?-x:x;
}
int n, m;
ll ans;
const int N = 100005;
namespace Heap
{
int siz[N], sum[N], dis[N];
struct node {int fa, l, c, ch[2], to;} t[N];
int find(int x) {if(t[x].fa != x) return t[x].fa = find(t[x].fa); return x;}
int Merge(int x, int y)
{
if(!x || !y) return x+y;
if(t[x].c < t[y].c) swap(x, y);
rs = Merge(rs, y); t[rs].fa = t[ls].fa = t[x].fa = x;
if(dis[rs] > dis[ls]) swap(ls, rs);
dis[x] = dis[rs]+1;
sum[x] = sum[ls]+sum[rs]+t[x].c;
siz[x] = siz[ls]+siz[rs]+1;
return x;
}
void Pop(int x)
{
t[ls].fa = ls, t[rs].fa = rs;
t[x].fa = Merge(ls, rs);
}
}
int main()
{
rea(n), rea(m);
using Heap::t;using Heap::Merge;
using Heap::find;using Heap::Pop;
using Heap::siz;using Heap::sum;
for(R int i = 1; i <= n; ++i)
rea(t[i].to), rea(t[i].c), rea(t[i].l),
t[i].fa = i, sum[i] = t[i].c, siz[i] = 1;
for(R int i = n; i >= 1; --i)
{
int rt1 = find(i), to = t[i].to;
ans = max(ans, t[i].l*1ll*siz[rt1]);
if(to) Merge(rt1, find(to));
while(sum[find(to)] > m) Pop(find(to));
}
printf("%lld\n", ans);
return 0;
}