[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
", ans);
	return 0;
}
原文地址:https://www.cnblogs.com/heanda/p/12374632.html