题目



其中n,q≤500000n,q\leq 500000n,q≤500000

题目大意

让你维护一个堆。支持一下操作:

在某个点的下面加上另一个点,然后进行上浮操作。

询问某一点的权值。


思考历程

一眼看这题,诶,不就是那道中学生数据结构题吗?

直接树链剖分,然后splay一波搞定!

思想还是很简单的!

但是感觉有点长……


正解

上面的这个解法算是一个正解吧。

但是我还是没打,因为代码可能很长……(想一想,又树链剖分,又splay的有点麻烦)

然后这题LCT也可以做!就是LCT和一个splay!由于LCT本来就是用splay来实现的,所以,在打板的时候还比较容易,只不过……

先不要说。现在说一说解法。

我们知道,上浮操作其实就是轮换操作。但是,如果我们直接在LCT中轮换,那么我们会将点的位置改变,不只是权值的改变!

所以说,我们要再用一个splay来维护一下链上点的权值。对于LCT上的每一条链(也就是LCT中的每一个splay),另外维护一个以权值为关键字的splay。这两个splay其中的顺序是一一对应的。当我们要轮换的时候,只需要轮换那个splay中的值。在查询的时候查与其对应位置的值就好了。

不过再想想,代码还是有点复杂的,虽然比较简单。我们再进行LCT操作的时候,我们还要维护一下那些splay,所以要加一些东西!当然,在宏观的意义上,这个还是比较好理解的。

然后还有一个比较简单的做法:离线,树链剖分和权值线段树!

这个似乎比较好打很多……在这里理清一下思路。

我们先将整棵树建出来(当然,是树的最后形态),然后树链剖分剖成许多条重链!

对于每一条重链,我们分别维护一个权值线段树。

在我们进行操作的时候,如果是询问,那就直接通过它在重链中的位置,在权值线段树中把它给找出来。

如果是插入一个点,那就将其加入权值线段树中(之前是无限大),然后和重链顶端的父亲上面的值对比一下,如果比它小,那就进行替换,然后继续往上跳。

其实不一定要使用权值线段树,平衡树也是一个不错的选择……

总感觉这题的正解有好多……


代码

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 1000000
#define Q 500000
#define MAX 1000001
int n,nn,q;
struct EDGE{
int to;
EDGE *las;
} e[N+1];
int ne;
EDGE *last[N+1];
inline void link(int u,int v){
e[++ne]={v,last[u]};
last[u]=e+ne;
}
int a[N+1];
struct Oper{
bool op;
int x;
} o[Q+1];
int fa[N+1],siz[N+1],dep[N+1],hs[N+1];
void init1(int);
int top[N+1];
void init2(int,int);
struct Segment_Tree{
int l,r;
int sum;
} seg[20000001];
int cnt;
void add(int,int,int,int,int);
int kth(int,int,int,int);
int tos[N+1];//tos[i]表示i所在的重链对应的线段树根节点的编号
inline void insert(int);
int main(){
freopen("heap.in","r",stdin);
freopen("heap.out","w",stdout);
scanf("%d%d",&n,&q);
for (int i=1;i<=n;++i){
scanf("%d%d",&fa[i],&a[i]);
if (fa[i])
link(fa[i],i);
}
nn=n;
for (int i=1;i<=q;++i){
int op;
scanf("%d",&op);
if (op==1){
++nn;
scanf("%d%d",&fa[nn],&a[nn]);
link(fa[nn],nn);
o[i]={0,nn};
}
else{
int x;
scanf("%d",&x);
o[i]={1,x};
}
}
init1(1);
init2(1,1);
for (int i=1;i<=nn;++i)
if (top[i]==i){
tos[i]=++cnt;
seg[cnt]={0,0,0};
}
for (int i=1;i<=nn;++i)
tos[i]=tos[top[i]];
for (int i=1;i<=n;++i)
add(tos[i],1,MAX,a[i],1);
for (int i=1;i<=q;++i)
if (o[i].op==0)
insert(o[i].x);
else
printf("%d\n",kth(tos[o[i].x],1,MAX,dep[o[i].x]-dep[top[o[i].x]]+1));
return 0;
}
void init1(int x){
dep[x]=dep[fa[x]]+1;
siz[x]=1;
for (EDGE *ei=last[x];ei;ei=ei->las){
init1(ei->to);
siz[x]+=siz[ei->to];
if (siz[ei->to]>siz[hs[x]])
hs[x]=ei->to;
}
}
void init2(int x,int t){
top[x]=t;
if (hs[x])
init2(hs[x],t);
for (EDGE *ei=last[x];ei;ei=ei->las)
if (ei->to!=hs[x])
init2(ei->to,ei->to);
}
void add(int k,int l,int r,int x,int c){
seg[k].sum+=c;
if (l==r)
return;
int mid=l+r>>1;
if (x<=mid){
if (!seg[k].l)
seg[k].l=++cnt;
add(seg[k].l,l,mid,x,c);
}
else{
if (!seg[k].r)
seg[k].r=++cnt;
add(seg[k].r,mid+1,r,x,c);
}
}
int kth(int k,int l,int r,int x){
if (l==r)
return l;
int mid=l+r>>1;
if (x<=seg[seg[k].l].sum)
return kth(seg[k].l,l,mid,x);
return kth(seg[k].r,mid+1,r,x-seg[seg[k].l].sum);
}
inline void insert(int x){
add(tos[x],1,MAX,a[x],1);
int v=a[x];
while (fa[top[x]]){
int y=fa[top[x]],mn=kth(tos[y],1,MAX,dep[y]-dep[top[y]]+1);//找出重链的父亲的值
if (v<mn){
//交换
add(tos[x],1,MAX,v,-1);
add(tos[x],1,MAX,mn,1);
add(tos[y],1,MAX,mn,-1);
add(tos[y],1,MAX,v,1);
x=y;
}
else
break;
}
}

总结

树链剖分真是一个好东西……

LCT真是一个好东西……

……

[JZOJ5977] 【清华2019冬令营模拟12.15】堆的更多相关文章

  1. JZOJ[5971]【北大2019冬令营模拟12.1】 party(1s,256MB)

    题目 题目大意 给你一棵树,在树上的某一些节点上面有人,要用最小的步数和,使得这些人靠在一起.所谓靠在一起,即是任意两个人之间的路径上没有空的节点(也就是连在一起). N≤200N \leq 200N ...

  2. jzoj5990. 【北大2019冬令营模拟2019.1.6】Bear (状压dp)

    题面 题解 我永远讨厌dp.jpg 搞了一个下午优化复杂度最后发现只要有一个小trick就可以A了→_→.全场都插头dp就我一个状压跑得贼慢-- 不难发现我们可以状压,对于每一行,用状态\(S\)表示 ...

  3. jzoj5991. 【北大2019冬令营模拟2019.1.6】Juice

    题面 题解 好迷-- //minamoto #include<bits/stdc++.h> #define R register #define ll long long #define ...

  4. jzoj5989. 【北大2019冬令营模拟2019.1.6】Forest (set)

    题面 题解 为了一点小细节卡了一个下午--我都怕我瞎用set把电脑搞炸-- 观察一次\(1\)操作会造成什么影响,比如说把\(A[i]\)从\(x\)改成\(y\): \(D[x]\)会\(-1\), ...

  5. jzoj5984. 【北大2019冬令营模拟2019.1.1】仙人掌 (分块)

    题面 题解 数据结构做傻了.jpg 考虑每一个节点,它的儿子的取值最多只有\(O(\sqrt {m})\)种,那么可以用一个双向链表维护儿子的所有取值以及该取值的个数,那么对儿子节点修改一个值就是\( ...

  6. jzoj5983. 【北大2019冬令营模拟2019.1.1】多边形 (组合数学)

    这其实是道打表题--你看我代码就知道了-- 咳咳来点严谨证明好了-- 前方高能请注意 首先,正多边形近似于圆,可以看做在圆里内接多边形.圆内接多边形最多只有三个锐角.因为凸多边形的外角和为\(360\ ...

  7. noip模拟12[简单的区间·简单的玄学·简单的填数]

    noip模拟12 solutions 这次考试靠的还是比较好的,但是还是有不好的地方, 为啥嘞??因为我觉得我排列组合好像白学了诶,文化课都忘记了 正难则反!!!!!!!! 害没关系啦,一共拿到了\( ...

  8. 【GDOI2016模拟3.15】基因合成(回文串+性质+DP)

    [GDOI2016模拟3.15]基因合成 题意: 给一个目标串,要求从空串进行最少的操作次数变成目标串,操作有两种: 在串的头或尾加入一个字符. 把串复制一遍后反向接到串的末尾. 因为有回文操作,所以 ...

  9. Tencent Cloud Developers Conference(2018.12.15)

    时间:2018.12.15地点:北京朝阳悠唐皇冠假日酒店

随机推荐

  1. Qt无边框窗口的移动、拉伸边框、鼠标滚轮缩放大小

    主要是处理窗口上鼠标的几个事件,具体代码请看下面的截图, 完整代码的下载链接在此:http://download.csdn.net/detail/beyond0824/9657110, 本示例代码中, ...

  2. iOS开发静态库冲突——如何查看静态库(.O)中方法名

    1.bug产生 应用第三方静态库之后提示冲突错误: 2.bug分析 一般会提示哪两个库冲突: CameraShowGLView.o是自己创建的类编译生成的: libLechangeSDK.a是添加的静 ...

  3. Codeforces 1167A-Telephone Number

    题目链接:http://codeforces.com/problemset/problem/1167/A 思路:检索前面0 ~(n −11)个字符中是否有 8 即可. AC代码: #include&l ...

  4. mysql分区管理语句

    1.key分区语句: ALTER TABLE order_info PARTITION BY KEY(orderSn) PARTITIONS 127; 2.rang分区语句: ALTER TABLE ...

  5. jpa现有接口方法说明 (转https://www.cnblogs.com/rulian/p/6557471.html)

    一.接口方法整理速查 下表针对于简单查询,即JpaRepository接口(继承了CrudRepository接口.PagingAndSortingRepository接口)中的可访问方法进行整理.( ...

  6. C puts,gets

    1.  char *gets(char *s); 从输入缓冲区中读取一个字符串存储到字符指针变量 str 所指向的内存空间 注意事项:空间不检查合理:要使用功能必须准备足够大的空间: #include ...

  7. Go, JS和Websocket

    JS中建立Websocket连接 var ws = new WebSocket("ws://hostname/path", ["protocol1", &quo ...

  8. canvas插入图片设置背景,渐变

    ##在canvas中插入图片(需要image对象) 1.canvas操作图片时,必须要等图片加载完才能操作 2.drawImage(image, x, y, width, height) 其中 ima ...

  9. leetcode-第10周双周赛-5080-查找两颗二叉搜索树之和

    题目描述: 自己的提交: class Solution: def twoSumBSTs(self, root1: TreeNode, root2: TreeNode, target: int) -&g ...

  10. mysql 新特性之geometry

    1.获取矩形两个点的数据(左上角和右下角) SELECT  *    FROM    t_location    WHERE   MBRContains                    (    ...