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

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

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

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

首先一开始每个忍者可以自己派遣自己出去,贡献就是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. Spring Boot + Spring Data JPA + PostgreSQL

    最近在用Java重写之前实习生用.netcore写的微信后台应用. 规定用Spring Boot框架,PostgreSQL数据库.之前一直习惯于基于XML的Spring app,也没用过Postgre ...

  2. FFmpeg编码详细流程

    FFmpeg在编码一个视频的时候的函数调用流程.为了保证结构清晰,其中仅列出了最关键的函数,剔除了其它不是特别重要的函数. 函数背景色 函数在图中以方框的形式表现出来.不同的背景色标志了该函数不同的作 ...

  3. MVC入门——删除页

    添加Action DeleteUserInfo using System; using System.Collections.Generic; using System.Linq; using Sys ...

  4. 二维码、条形码扫描——使用Google ZXing

    我在项目中用到了二维码扫描的技术,用的是Google提供的ZXing开源项目,它提供二维码和条形码的扫描.扫描条形码就是直接读取条形码的内容,扫描二维码是按照自己指定的二维码格式进行编码和解码. 可以 ...

  5. IOS 单元测试

    本文转载至 http://blog.csdn.net/fengsh998/article/details/8109293 IOS 自带单元测试. 1.在创建时,将include Unit Tests钩 ...

  6. Linux 如何搭建Lamp的服务环境

    在介绍虚拟机的安装使用之前,我们先认识一下LINUX中LAMP是什么的缩写 L--Linux   A--Apache   M--Mysql  P--PHP 虚拟机的安装百度有详细的介绍,非常简单的,这 ...

  7. OBS桌面视频直播软件/推流工具使用指南

    OBS 操作指南 什么是OBS? Open Broadcaster Software 是一款好用的互联网流媒体直播内容输入作软件. OBS使用是否收费? 不收费,这个程序和它的源代码都是免费的. OB ...

  8. EasyPusher安卓Android手机直播推送之RTSP流媒体协议流程

    EasyPusher移动端推送同我们平时用的RTSP直播推送流程一样,都是采用标准RTSP/RTP推送流程:ANNOUNCE->SETUP->PLAY->RTP/RTCP->T ...

  9. Markov and Chebyshev Inequalities and the Weak Law of Large Numbers

    https://www.math.wustl.edu/~russw/f10.math493/chebyshev.pdf http://www.tkiryl.com/Probability/Chapte ...

  10. Axure实现Tab选项卡切换功能

    这几天用Axure画原型图的过程中,须要实现Tab选项卡切换的效果,但Axure中并没有类似于Tab控件的部件,所以能够用Axure中的动态面板(Dynamic Panel)来实现. 本文以已经汉化的 ...