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

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

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

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

首先一开始每个忍者可以自己派遣自己出去,贡献就是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. python 修饰符(转载)

    首先一个修饰符的实例: #!/usr/bin/env python def run(fn): def do_hello(): print "begin..." fn() print ...

  2. SQL创建触发器

    更新: CREATE TRIGGER `r_users_1` AFTER UPDATE ON `users` FOR EACH ROW update `wxusers` set status=NEW. ...

  3. dokcer3

    安装好的文件位置: /usr/sbin/nginx:主程序 /etc/nginx:存放配置文件 /usr/share/nginx:存放静态文件 /var/log/nginx:存放日志 其实从上面的根目 ...

  4. bzoj4485: [Jsoi2015]圈地

    思维僵化选手在线被虐 其实应该是不难的,题目明显分成两个集合,要求是不同集合的点不能联通 先假设全选了,然后二分图最小割,相邻两个点直接连墙的费用就可以了 #include<cstdio> ...

  5. Java8初体验(2):Stream语法详解

    原文出处: 一冰_天锦 上篇文章Java8初体验(1):lambda表达式语法比较详细的介绍了lambda表达式的方方面面,细心的读者会发现那篇文章的例子中有很多Stream的例子.这些Stream的 ...

  6. python实现路由追踪,并生成追踪图片

    #!/usr/bin/env python # -*- coding: utf-8 -*- import os,sys,time,subprocess import warnings,logging ...

  7. php-循环普通数组和关联数组

    <?php //循环普通数组 $arr=array("杭州","成都","拉萨"); $arrlength=count($arr); ...

  8. 洛谷【P839】【NOI导刊】——数页码

    题面 一道找规律好题... 首先,我们肯定只能一位一位的统计答案,考虑从高位向低位统计,显然这样要方便的多. 对于第i位,我们统计从$a[i+1]*10^i+0$到$a[i+1]*10^i+a[i]* ...

  9. 编译Thrift

    按照 https://syslint.com/blog/tutorial/how-to-install-apache-thrift-on-ubuntu-14-04/ 进行, 编译时出现错误 make[ ...

  10. typeof详解

    typeof operator 返回了表示对象类型的字符串 下表列出了typeof可能的返回值. Type Result Undefined "undefined" Null &q ...