【BZOJ】【P2809】【APIO2012】【dispatching】【题解】【左偏树】

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2809

题意就是给定一棵树,每个节点有两个属性C(薪水),L(领导力),找出一个节点u和u的子节点S(sigma S.C  <=m)使得 u.L*|S|最大

显然根结点需要枚举,看看数据……n<=1e5 看来要logn 求满足条件的最多的子节点了……蒟蒻首先想到了背包,然后发现我是SX……

因为价值都是1,所以很明显从小到大选人最优 = =、于是就是求以某树为根节点的子节点中从小到大的前缀和<=m的最大个数(描述渣……)

于是蒟蒻想到了dfs序+树套树or主席树  然后不会写……QAQ 我好弱啊啊啊啊啊

再仔细想想,如果在同一个根结点下,A的C比B的C大,那么选A不可能比选B更优,在不能选取时应优先去除A,这就与堆有关,而且计算根结点的值需要合并其所有子树所形成的堆,可合并堆?那就左偏树吧!

dfs自底向上枚举节点作为管理员,每当sum>m的时候就pop堆中最大元素,子节点枚举完成后就合并成根节点,边走边记录ans的最大值,然后就完了,代码不长,应该很容易读懂

Code:

#include<cstdio>
#include<vector>
#include<climits>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1e5+10;
int n,m;
int sor[maxn],lead[maxn],tot;
struct node{
	int val,dis;
	node *c[2];
}*Null,poor[maxn<<2];
namespace TRT{
	struct LeftistTree{
		node *root;
		int sum,size;
		void init(){
			sum=size=0;
			root=Null;
		}			
		node *merge(node *&A,node *&B){
			if(A==Null)return B;
			if(B==Null)return A;
			if(A->val<B->val)
				swap(A,B);
			A->c[1]=merge(A->c[1],B);
			if(A->c[0]->dis<A->c[1]->dis)
				swap(A->c[0],A->c[1]);
			A->dis=A->c[1]->dis+1;
			return A;
		}
		void merge(LeftistTree &B){
			size+=B.size;
			sum+=B.sum;
			root=merge(root,B.root);
		}
 	   void insert(int x) {
    	    node *p = &poor[++tot];
        	++size;
        	sum += x;
        	p->val = x;
        	p->dis = 0;
        	p->c[0] = p->c[1] = Null;
        	root = merge(root, p);
    	}
		int top(){
			return root->val;
		}
		void pop(){
			size--;
			sum-=root->val;
			root=merge(root->c[0],root->c[1]);
		}
	}t[maxn];
}
using namespace TRT;
namespace TOPL{
	vector<int>G[maxn];
	void add(int u,int v){
		G[u].push_back(v);
	}
	int root=1;
	void init(){
		Null=new node;Null->dis=-1;
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++){
			int a,b,c;scanf("%d%d%d",&a,&b,&c);
			if(a)add(a,i);
			else root=i;
			sor[i]=b;
			lead[i]=c;
		}
	}
	long long ans=0;
	void dfs(int u){
		t[u].init();
		t[u].insert(sor[u]);
		for(int i=0;i<G[u].size();i++){
			int v=G[u][i];
			dfs(v);
			t[u].merge(t[v]);
			while(t[u].sum>m)
				t[u].pop();
		}
		ans=max(ans,(long long)lead[u]*t[u].size);
	}
	void solve(){
		init();
		dfs(root);
		cout<<ans<<endl;
	}
}
int main(){
	TOPL::solve();
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值