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

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

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

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

首先一开始每个忍者可以自己派遣自己出去,贡献就是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. 字符串转换成js的日期格式

    js字符串转日期格式 ,JavaScript字符串转日期格式 大家都知道JS是根据结果来确定数据类型的. 当然我们也是可以转化的,下面我就介绍两种关于JS字符串类型转换成日期类型的方法, 我个人比较喜 ...

  2. Cannot find autoconf. Please check your autoconf installation and the $PHP_AUTOCONF environment variable. Then, rerun this script.

    运行/usr/local/webserver/php/bin/phpize时出现: Configuring for: PHP Api Version: 20041225 Zend Module Api ...

  3. switch中的case不加break执行情况

    输出结果:230 分析,switch先匹配一个case满足$a,然后执行case里面的语句,直到遇到break,否则一直往下执行 <?php $a = ; switch($a){ : echo ...

  4. listview 下拉刷新

    http://blog.csdn.net/lancees/article/details/7776853

  5. java设计模式之综述

    一.什么是设计模式 设计模式是一套被反复使用的.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了重用代码.让代码更容易被他人理解.保证代码可靠性. 毫无疑问,设计模式于己于他人于系 ...

  6. 九度OJ 1121:首字母大写 (字符串处理)

    时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:2865 解决:1007 题目描述: 对一个字符串中的所有单词,如果单词的首字母不是大写字母,则把单词的首字母变成大写字母. 在字符串中,单词 ...

  7. Splits a tensor into sub tensors

    https://www.tensorflow.org/api_docs/python/tf/split # 'value' is a tensor with shape [5, 30] # Split ...

  8. Cocos2d-x动画播放(序列帧)

    简介 Cocos2d-x中,动画的具体内容是依靠精灵显示出来的,为了显示动态图片,我们需要不停切换精灵显示的内容,通过把静态的精灵变为动画播放器从而实现动画效果.动画由帧组成,每一帧都是一个纹理,我们 ...

  9. Struts多个文件上传

    Struts2多个文件上传 10级学员 韩晓爽课堂笔记 多个文件上传分为List集合和数组,下面我们着重介绍一下list集合的上传.都大同小异. 一 介绍 1. 在struts2文件上传的时候要先导入 ...

  10. atol的实现【转】

    本文转载自:http://blog.csdn.net/cwqbuptcwqbupt/article/details/7518582 看了atol的实现,发现char到int的转换比较奇怪:c = (i ...