首先把这个树建出来,然后每一次操作,只能选中一棵子树。对于树根,他的领导力水平是确定的,然后他更新答案的情况就是把他子树内薪水最少的若干个弄出来。

问题在于怎么知道一棵子树内薪水最少的若干个分别是谁。

考虑到原本就是从别的博客过来的,先天知道了这是左偏树。

那么就是每次合并若干个忍者吗?

首先一开始每个忍者可以自己派遣自己出去,贡献就是1x自己的领导力水平。

然后逐个合并每个忍者和自己的直接领导,合并之后就保证了自己的直接领导一定有被选中!新的贡献就是领导的领导力水平x目前堆最小的若干个元素?

很容易发现,薪水较高的忍者会很快被淘汰掉。

所以可以设想一个最大堆,每次两棵左偏树维护的还有整棵树的薪水和。

当两棵左偏树合并时,直到他们的薪水和低于限制,把堆根弹出并更新薪水和。

好,那就动手吧。

需要注意的是,要从叶子节点向上合并。


出了一些问题,的确应该是从叶子节点向上合并,但是应该是当这个节点出度为0的时候才统计。

合并可并堆自然不用说,把两组接在一起就可以了。在Pop的时候可能会把领导节点丢失掉,但是领导节点所在的块是依然存在的。

所以一开始高于薪水的就直接不建立左偏树,只建立并查集。

每次合并,就把当前忍者所在的并查集根对应的左偏树和领导的并查集根对应的左偏树合并(当领导左偏树存在时,要是不存在则只是直接把并查集根指向领导的根)

一个块的领导力水平和总的薪水和由并查集来确定,Pop的时候其中一棵子树接在另一棵子树上,并查集的根应该指向这个新的根,同时把领导力水平覆盖过去,薪水和为原薪水和减去Pop的节点。


终于过了啊!关键点是在于合并左偏树前维护并查集的整体信息,然后Pop节点时把并查集的信息下传。


#include<bits/stdc++.h>
using namespace std;
typedef long long ll; int solve(); int main() {
#ifdef Yinku
freopen("Yinku.in","r",stdin);
#endif // Yinku
solve();
} int n,m; const int MAXN=100005;
int tot,v[MAXN],l[MAXN],r[MAXN],d[MAXN],f[MAXN];
int sum[MAXN],siz[MAXN];
//编号为x的左偏树的薪水总和为sum[x]
int lead[MAXN];
//编号为x的左偏树的领导(不一定是树根)的领导力是lead[x] int outd[MAXN];
//出度 int ld[MAXN]; void show() {
printf("---\n");
printf("ii=");
for(int i=1; i<=n; i++) {
printf("%3d ",i);
}
printf("\n"); printf("vv=");
for(int i=1; i<=n; i++) {
printf("%3d ",v[i]);
}
printf("\n"); printf("su=");
for(int i=1; i<=n; i++) {
printf("%3d ",sum[i]);
}
printf("\n"); printf("le=");
for(int i=1; i<=n; i++) {
printf("%3d ",lead[i]);
}
printf("\n"); printf("ld=");
for(int i=1; i<=n; i++) {
printf("%3d ",ld[i]);
}
printf("\n"); printf("ls=");
for(int i=1; i<=n; i++) {
printf("%3d ",l[i]);
}
printf("\n"); printf("rs=");
for(int i=1; i<=n; i++) {
printf("%3d ",r[i]);
}
printf("\n"); printf("si=");
for(int i=1; i<=n; i++) {
printf("%3d ",siz[i]);
}
printf("\n---\n");
} //这其实是一片左偏树森林
struct Leftist_Tree {
int Merge(int x,int y) {
//将两棵左偏树,返回他们的新根
//当其中一棵是空树,直接返回另一棵
if(!x||!y)
return x+y; // printf("Merge %d %d\n",x,y);
//show(); //此处符号决定是最小堆还是最大堆
//标记是不是更改过领导为y
int haveswap=0;
if(v[x]<v[y]||(v[x]==v[y]&&x>y)){
swap(x,y);
}
//维持x不比y小,相等则把大的那个作为根
r[x]=Merge(r[x],y);
//保证左子树比右子树高 //维持并查集的性质
f[r[x]]=x;
if(d[l[x]]<d[r[x]])
swap(l[x],r[x]);
d[x]=d[r[x]]+1; //show();
//printf("New root=%d\n",x);
return x;
}
void Init(int n) {
//使用v[]中的值初始化一片左偏树-并查集森林
tot=n;
for(int i=1; i<=n; i++) {
l[i]=r[i]=d[i]=0;
//不合要求的一开始就不建左偏树
if(v[i]<=m){
sum[i]=v[i];
siz[i]=1;
}
else{
v[i]=0;
sum[i]=0;
siz[i]=0;
}
//只建立并查集
f[i]=i;
}
} int Get_root(int x){
//查找编号为x的节点所在的左偏树的根的序号,不需要可以删除
int r=x;
while(f[r]!=r)
r=f[r];
//路径压缩,直接指向他们的根
int k;
while(f[x]!=r){
k=f[x];
f[x]=r;
x=k;
}
return r;
}
int Pop(int x) {
//将两个子树合并,相当于删除了堆顶
//删除一个元素,要把新的树根的薪水和更新
//其次要把新树根的领导力也更新 //把一棵树的堆顶删除
int rt=Merge(l[x],r[x]);
if(v[x]){
sum[rt]=sum[x]-v[x];
//本身一开始v[x]就没进来的,不需要减去siz
siz[rt]=siz[x]-1;
}
lead[rt]=lead[x];
f[x]=f[l[x]]=f[r[x]]=f[rt]=rt;
return rt;
}
}lt; void Merge2(int p1,int p2){
if(p1==p2)
exit(-1);
int newsum=sum[p1]+sum[p2];
int newsiz=siz[p1]+siz[p2];
int newlead=lead[p1];
sum[p1]=sum[p2]=newsum;
siz[p1]=siz[p2]=newsiz;
lead[p1]=lead[p2]=newlead;
} queue<int> pq; ll ans=0; void Update_Ans(int x){
//编号为x的左偏树需要更新答案
int fx=lt.Get_root(x);
//现在这个忍者的根在fx
ll TMP=1ll*siz[fx]*lead[fx];
if(TMP>ans){
ans=TMP;
}
return;
} int solve() {
scanf("%d%d",&n,&m);
memset(outd,0,sizeof(outd));
for(int i=1; i<=n; i++) {
scanf("%d%d%d",&ld[i],&v[i],&lead[i]);
if(v[i]<=m) {
ans=max(ans,(ll)lead[i]);
}
outd[ld[i]]++;
} lt.Init(n);
//show(); for(int i=1;i<=n;i++){
if(outd[i]==0)
pq.push(i);
} while(!pq.empty()){
int i=pq.front();
if(ld[i]==0)
break;
pq.pop();
outd[ld[i]]--; //合并他和他的领导,找到两个并查集合并
int p1=lt.Get_root(ld[i]);
int p2=lt.Get_root(i);
//if(p1==p2)
//exit(-1);
//合并,新的根是x
Merge2(p1,p2);
//show();
//cout<<"!!!\n\n\n"<<endl;
int x=lt.Merge(p1,p2);
//show();
//合并完之后sum超过了m,要弹出一些节点,根据定义不会出错
while(sum[x]>m) {
x=lt.Pop(x);
} //cout<<"AP!"<<endl;
//show(); if(outd[ld[i]]==0){
//某个忍者和他所有的下属合并完成,更新他
Update_Ans(ld[i]);
pq.push(ld[i]);
}
}
printf("%lld\n",ans);
return 0;
}

洛谷 - P1552 - 派遣 - 左偏树 - 并查集的更多相关文章

  1. 洛谷 - P3377 - 【模板】左偏树(可并堆) - 左偏树 - 并查集

    https://www.luogu.org/problemnew/show/P3377 左偏树+并查集 左偏树维护两个可合并的堆,并查集维护两个堆元素合并后可以找到正确的树根. 关键点在于删除一个堆的 ...

  2. P1552 派遣 左偏树

    左偏树就是一个应该用堆维护的区间,然后需要进行合并操作而发明的算法,其实这个算法没什么难的,和树剖有点像,维护几个数值,然后递归回来的时候就可以修改. 题干: 题目背景 在一个忍者的帮派里,一些忍者们 ...

  3. 洛谷 P3377 模板左偏树

    题目:https://www.luogu.org/problemnew/show/P3377 左偏树的模板题: 加深了我对空 merge 的理解: 结构体的编号就是原序列的位置. 代码如下: #inc ...

  4. zoj 2334 Monkey King/左偏树+并查集

    原题链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1389 大致题意:N只相互不认识的猴子(每只猴子有一个战斗力值) 两只 ...

  5. bzoj 1455: 罗马游戏 左偏树+并查集

    1455: 罗马游戏 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 668  Solved: 247[Submit][Status] Descriptio ...

  6. HDU 1512 Monkey King(左偏树+并查集)

    [题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=1512 [题目大意] 现在有 一群互不认识的猴子,每个猴子有一个能力值,每次选择两个猴子,挑出他们所 ...

  7. 【BZOJ 1455】 1455: 罗马游戏 (可并堆-左偏树+并查集)

    1455: 罗马游戏 Description 罗马皇帝很喜欢玩杀人游戏. 他的军队里面有n个人,每个人都是一个独立的团.最近举行了一次平面几何测试,每个人都得到了一个分数. 皇帝很喜欢平面几何,他对那 ...

  8. 【bzoj1455】【罗马游戏】左偏树+并查集(模板)

    Description 罗马皇帝很喜欢玩杀人游戏. 他的军队里面有n个人,每个人都是一个独立的团.最近举行了一次平面几何测试,每个人都得到了一个分数. 皇帝很喜欢平面几何,他对那些得分很低的人嗤之以鼻 ...

  9. HDU 1512 Monkey King (左偏树+并查集)

    题意:在一个森林里住着N(N<=10000)只猴子.在一开始,他们是互不认识的.但是随着时间的推移,猴子们少不了争斗,但那只会发生在互不认识 (认识具有传递性)的两只猴子之间.争斗时,两只猴子都 ...

随机推荐

  1. 面向对象基础——String类

    String类的两种实例化方法  A:直接赋值 public class StringDemo01{ public static void main(String args[]){ String na ...

  2. bzoj-2251 外星联络

    题意: 给出一个字符串,求出现次数超过1的子串的出现个数. 字符串长度<=3000: 题解: 题目问的是子串的个数.那么首先我们要找到全部的子串. 而字符串的全部后缀的前缀能够不重不漏的表示全部 ...

  3. Sping框架概述

    一.什么是spring框架 spring是J2EE应用程序框架,是轻量级的IoC和AOP的容器框架,主要是针对javaBean的生命周期进行管理的轻量级容器,可以单独使用,也可以和Struts框架,i ...

  4. 微信分享配置(js-sdk)

    现在的微信分享给朋友-分享到朋友圈 链接带有自定义的title.描述.图片,需要配置js-sdk(地址:mp.weixin.qq.com)微信文档 需要后台配置config的参数,返回给前台 1)de ...

  5. ReentrantLock和Synchronized

    1 synchronized 1.1 一旦没有获取到就只能一直等待 A和B都获取同一个对象锁,如果A获取了,B没有获取到,那么在A释放该锁之前,B只能无穷等待下去. 1.2 synchronized是 ...

  6. (t,p,o) t:p>=o there cannot be more consumer instances in a consumer group than partitions

    https://kafka.apache.org/intro.html Kafka as a Messaging System How does Kafka's notion of streams c ...

  7. hsv hsb rgb lab

    lab  欧式距离 反映 人类所能感受到的这两种颜色的差异

  8. SURF matlab 检测函数使用

    1.这篇介绍SURF检测blob的函数. 函数/Functions 函数名称:detectSURFFeatures 功能:利用The Speeded-Up Robust Features(SURF)算 ...

  9. ThinkPHP RBAC权限管理机制

    RBAC是ThinkPHP很好用的后台权限管理的,话不多说,实现方法如下,也方便以后自己查询使用: 1.新建4个数据库表 self_role权限表 CREATE TABLE `self_role` ( ...

  10. HDU2829 Lawrence —— 斜率优化DP

    题目链接:https://vjudge.net/problem/HDU-2829 Lawrence Time Limit: 2000/1000 MS (Java/Others)    Memory L ...