luogu P1552 [APIO2012]派遣 题解--可并堆/贪心

  • 题目链接:

    https://www.luogu.org/problemnew/show/P1552

  • 分析:

    一开始愣是没看懂题,后面发现就是你要找一个树上点集使得各点权值之和小于\(M\),并且找一个点集的公共祖先\(Anc\)(管理者),使\(Anc\)的领导力乘以点集大小最大

    一开始想DP,一看数据范围,我们可以稍微暴力一点,枚举每个点作为管理者的答案最大值,我们只要在子树中权值最小的点选起使权值之和小于\(M\)就可以了,一下问题简单了许多.

    再暗中观察分析,发现这个信息是可以维护转移的,在两颗子树合并时可以用大根堆搞一搞.我们不妨先对树进行\(dfs\),中途不断合并权值信息.

    \(dfs\) \(x\)节点时,以\(x\)为根建一个大根堆称为x的代表堆,\(dfs\)\(x\)的一个子树回溯到\(x\)时,将子树的代表堆与\(x\)的代表堆进行合并,同时将两堆权值之和相加.若权值之和大于\(m\),由于是大根堆,不断\(pop\)堆顶直至小于\(m\),满足最优子结构.

  • 注意

    本题思维容易但要注意一些问题:

    1. 虽说是X的代表堆,然而X可能会被\(pop\)这个堆,于是要用一个\(root\)记录代表堆的根,同时dfs返回该root值

    2. 可并堆可用斜堆或左偏堆 COGS上斜堆略胜一筹,luogu上却不尽人意

    3. 还有一些细节和小技巧,比如处理祖宗的最大领导力可以看代码

  • 代码(左偏树)

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <map>
#include <queue>
#define ri register int 
#define ll long long 
using namespace std;
const int maxn=103005;
const int inf=0x7fffffff;
template <class T>inline void read(T &x){
	x=0;int ne=0;char c;
	while(!isdigit(c=getchar()))ne=c=='-';
	x=c-48;
	while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+c-48;
	x=ne?-x:x;
	return ;
}
int n;
ll  m,leader[maxn],key[maxn],ans[maxn],anss=-inf;
int ls[maxn],rs[maxn],fa[maxn],cnt[maxn],npl[maxn];//可并堆
vector <int>son[maxn];
int get(int x){
	while(x!=fa[x])fa[x]=get(fa[x]);
	return fa[x];
}
int merge(int x,int y){
	if(!x)return y;
	if(!y)return x;
	if(key[x]<key[y])swap(x,y);
	fa[y]=x;
	rs[x]=merge(rs[x],y);
	if(!ls[x]||npl[ls[x]]<npl[rs[x]])swap(ls[x],rs[x]);
	if(!rs[x])npl[x]=0;
	else npl[x]=npl[rs[x]]+1;
	//swap(ls[x],rs[x]);
	return x;
}
int del(int now){
	fa[now]=-1;
	fa[ls[now]]=fa[rs[now]]=0;
	return merge(ls[now],rs[now]);
}
int dfs(int now,ll mx){//mx--祖先上的领导力最大值
	int s,root=now,ro_s;
	mx=max(mx,leader[now]);
	ans[now]=key[now],cnt[now]=1;
	anss=max(anss,leader[now]);
	for(ri i=0;i<son[now].size();i++){		
		s=son[now][i];		
		ro_s=dfs(s,mx);	
		ans[now]+=ans[s],cnt[now]+=cnt[s];
		root=merge(root,ro_s);	
		while(ans[now]>m&&cnt[now]) {
			ans[now]-=key[root];
			cnt[now]--;
			root=del(root);
		}
		anss=max(anss,cnt[now]*mx);
	}
	return root;
}
int main(){
	int pa;
	//freopen("dispatching.in","r",stdin);
	//freopen("dispatching.out","w",stdout);
	read(n),read(m);
	for(ri i=1;i<=n;i++){
		read(pa),read(key[i]),read(leader[i]);
		ls[i]=rs[i]=0,fa[i]=i,npl[i]=0;
		son[pa].push_back(i);
	}
	dfs(1,leader[1]);
	printf("%lld\n",anss);
	fclose(stdin);
	fclose(stdout);
	return  0;
}
posted @ 2018-07-03 22:03  Rye_Catcher  阅读(151)  评论(0编辑  收藏  举报