Bzoj2809:[Apio2012]dispatching:左偏樹

題目鏈接:2809:[Apio2012]dispatching

考慮對於以節點x爲根的一顆子樹,我們只要在m的限制下儘量多地選擇忍者即可,這個可以用大根堆維護

考慮x向x的父親y轉移時,相當於y的各顆子樹在m的限制下儘可能多的選擇忍者,顯然我們不能暴力重建堆,用可並堆logn維護即可,這裏我用的是左偏樹

每轉移到一個節點就更新一遍答案

#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
const int maxn=200010;
int n,m,tot=1,h[maxn],l[maxn];
int rt[maxn],cnt[maxn],root,num=0;
struct edge{int to,next;}G[maxn*6];
LL ans=0,cost[maxn],c[maxn];
struct LeftIstTree{
	struct treenode{
		int v,l,r,d;
		treenode(){v=l=r=d=0;}
		bool operator < (const treenode &a)const{return v<a.v;}
	};
	treenode t[maxn];
	int newnode(int x){
		num++; t[num].v=c[x]; return num; 
	}
	int merge(int a,int b){
		if(!a||!b)return a+b;
		if(t[a]<t[b])swap(a,b);
		t[a].r=merge(t[a].r,b);
		if(t[t[a].l].d<t[t[a].r].d)swap(t[a].l,t[a].r);
		t[a].d=t[a].r?t[t[a].r].d+1:0;
		return a;
	}
}lit;

void add(int x,int y){
	G[++tot].to=y;G[tot].next=h[x];h[x]=tot;
}

void dfs(int x){
	rt[x]=lit.newnode(x); cost[x]=c[x]; cnt[x]=1;
	if (c[x]<=m) ans=max(ans,1LL*l[x]);
	for (int i=h[x];i;i=G[i].next){
		int v=G[i].to; dfs(v);
		rt[x]=lit.merge(rt[x],rt[v]);
		cost[x]+=cost[v]; cnt[x]+=cnt[v];
		while (cost[x]>m) cost[x]-=lit.t[rt[x]].v,cnt[x]--,
						  rt[x]=lit.merge(lit.t[rt[x]].l,lit.t[rt[x]].r);
		ans=max(ans,1LL*l[x]*cnt[x]);
	}
}

int main(){
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;++i){
		int x; scanf("%d%lld%d",&x,&c[i],&l[i]);
		add(x,i); if (!x) root=i;
	}
	ans=0; dfs(root);
	printf("%lld\n",ans);
}


發佈了112 篇原創文章 · 獲贊 12 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章