题面

洛咕题面

思路

没错我就是要不走寻常路!

看看那些外层位置数据结构,必须二分的,$O(n\log^3 n)$的做法吧!

看看那些cdq分治/树状数组套线段树的,空间$O(n\log^2 n)$挤挤挤开不下的做法吧!

这些都不是最优秀的,我来写一种理论复杂度为时间$O(n\log n\log (m+n))$,空间$O(n\log (n+m))$的做法

我们首先考虑时间问题:为什么传统做法的复杂度是3个$log$的?

核心问题是他们有一个二分,否则无法处理第二种询问

那么可以看到第二种询问本质上就是关于区间中权值从小到大排序以后提取出某一位作为答案

考虑把内外层数据结构的意义交换:外层维护权值,内层维护位置

对于询问一:

这里就是简单查询,在外层数对应的区间范围内查询一共有多少个内层数上位于询问区间内的点即可

实现函数为$getk$

对于询问二:

首先,答案就是这个区间内排名为第$k$的数的权值

我们在外层树上二分查找,每次询问内层中外层当前节点的左儿子询问区间中节点个数

如果左边比需要的大就递归进入左边,否则需要的值先减去左边的总数,再进入右边

最后返回叶节点的权值

代码实现为$query$函数

对于修改三

删除再插入即可

对于询问四

我们首先查询,在给定区间内,比询问的数小的数有多少个(用$getk$函数)

然后,其实这个数量,等价于排名

所以我们把排名+1,再用$query$函数输出这个排名对应的数

对于询问五,基本与询问4等价,不赘述

综上,这里的核心思路其实就是:

比某个数小的数字的个数就是这个数的排名

实现的时候,注意外层和内层不同于常规树套树

使用指针or引用可以得到比较好的效果

同时,如果不离散化,时间复杂度和空间复杂度里面的那个$\log(n+m)$都会变成$\log 10^8$

不过也是可以过的

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cassert>
#include<queue>
using namespace std;
inline int read(){
int re=0,flag=1;char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') flag=-1;
ch=getchar();
}
while(isdigit(ch)) re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
return re*flag;
}
namespace spl{
int ch[6000010][2],w[6000010],siz[6000010],fa[6000010],cnt;
void update(int cur){
siz[cur]=siz[ch[cur][0]]+siz[ch[cur][1]]+1;
}
int newnode(int f,int val){
cnt++;
fa[cnt]=f;siz[cnt]=1;ch[cnt][0]=ch[cnt][1]=0;w[cnt]=val;
return cnt;
}
bool get(int cur){return ch[fa[cur]][1]==cur;}
void rotate(int x){
int f=fa[x],ff=fa[f],son=get(x);
ch[f][son]=ch[x][son^1];
if(ch[f][son]) fa[ch[f][son]]=f;
fa[f]=x;ch[x][son^1]=f;
fa[x]=ff;
if(ff) ch[ff][ch[ff][1]==f]=x;
update(f);update(x);
}
void splay(int x,int to,int &root){
for(int f;(f=fa[x])!=to;rotate(x)){
if(fa[f]!=to)
rotate((get(x)==get(f))?f:x);
}
if(!to) root=x;
}
void insert(int &cur,int val,int f,int &root){
if(!cur){
cur=newnode(f,val);
splay(cur,0,root);
return;
}
siz[cur]++;
if(w[cur]>=val) insert(ch[cur][0],val,cur,root);
else insert(ch[cur][1],val,cur,root);
}
int getpos(int cur,int val){
if(w[cur]==val) return cur;
if(w[cur]>val) return getpos(ch[cur][0],val);
else return getpos(ch[cur][1],val);
}
int pre(int root){
int u=ch[root][0];
while(ch[u][1]) u=ch[u][1];
return u;
}
void del(int val,int &root){
int cur=getpos(root,val);splay(cur,0,root);
if(!ch[cur][0]&&!ch[cur][1]){root=0;return;}
if(!ch[cur][0]&&ch[cur][1]){
fa[ch[cur][1]]=0;root=ch[cur][1];
siz[cur]=ch[cur][0]=ch[cur][1]=fa[cur]=0;
return;
}
if(ch[cur][0]&&!ch[cur][1]){
fa[ch[cur][0]]=0;root=ch[cur][0];
siz[cur]=ch[cur][0]=ch[cur][1]=fa[cur]=0;
return;
}
int p=pre(cur),suf=ch[cur][1];
splay(p,0,root);
assert(fa[cur]==p);
assert(ch[cur][0]==0);
ch[p][1]=suf;fa[suf]=p;update(p);
siz[cur]=ch[cur][0]=ch[cur][1]=fa[cur]=0;
}
int pre(int val,int &root){
int u=root,re=0,minn=1e9;
while(u){
if(w[u]>=val) u=ch[u][0];
else{
if(val-w[u]<minn){minn=val-w[u];re=u;}
u=ch[u][1];
}
}
splay(re,0,root);
return re;
}
int suf(int val,int &root){
int u=root,re=0,minn=1e9;
while(u){
if(w[u]<=val) u=ch[u][1];
else{
if(w[u]-val<minn){minn=w[u]-val;re=u;}
u=ch[u][0];
}
}
splay(re,0,root);
return re;
}
int query(int &root,int l,int r){
int x=pre(l,root),y=suf(r,root);
splay(x,0,root);splay(y,x,root);
return siz[ch[y][0]];
}
}
namespace ct{
int cnt,re;queue<int>q;
int newnode(){
if(!q.empty()){re=q.front();q.pop();return re;}
else return ++cnt;
}
void del(int num){
q.push(num);
}
}
namespace seg{
int root[1000010],ch[1000010][2];
void insert(int &cur,int l,int r,int pos,int val){//pos is value in original list, val is position
if(!cur){
cur=ct::newnode();
spl::insert(root[cur],-1e6,0,root[cur]);
spl::insert(root[cur],1e6,0,root[cur]);
}
spl::insert(root[cur],val,0,root[cur]);
if(l==r) return;
int mid=(l+r)>>1;
if(mid>=pos) insert(ch[cur][0],l,mid,pos,val);
else insert(ch[cur][1],mid+1,r,pos,val);
}
void del(int &cur,int l,int r,int pos,int val){
spl::del(val,root[cur]);
if(l^r){
int mid=(l+r)>>1;
if(mid>=pos) del(ch[cur][0],l,mid,pos,val);
else del(ch[cur][1],mid+1,r,pos,val);
}
if(spl::siz[root[cur]]==2){
ct::del(cur);
spl::del(-1e6,root[cur]);
spl::del(1e6,root[cur]);
ch[cur][0]=ch[cur][1]=root[cur]=0;
cur=0;
}
}
int getk(int &cur,int l,int r,int qx,int qy,int ql,int qr){
if(!cur) return 0;
if(l>=ql&&r<=qr) return spl::query(root[cur],qx,qy);
int mid=(l+r)>>1,re=0;
if(mid>=ql) re+=getk(ch[cur][0],l,mid,qx,qy,ql,qr);
if(mid<qr) re+=getk(ch[cur][1],mid+1,r,qx,qy,ql,qr);
return re;
}
int query(int cur,int l,int r,int pos,int qx,int qy){
if(l==r) return l;
int tmp=0;
if(ch[cur][0]) tmp=spl::query(root[ch[cur][0]],qx,qy);
int mid=(l+r)>>1;
if(tmp>=pos) return query(ch[cur][0],l,mid,pos,qx,qy);
else return query(ch[cur][1],mid+1,r,pos-tmp,qx,qy);
}
}
int a[50010],n,m,rt=0;
int main(){
using namespace seg;
n=read();m=read();
int i,t1,t2,t3,t4,tmp;
for(i=1;i<=n;i++){
a[i]=read();
insert(rt,0,1e8,a[i],i);
}
while(m--){
t1=read();
if(t1==1){
t2=read();t3=read();t4=read();
if(t4==0) puts("1");
else printf("%d\n",getk(rt,0,1e8,t2,t3,0,t4-1)+1);
}
if(t1==2){
t2=read();t3=read();t4=read();
printf("%d\n",query(rt,0,1e8,t4,t2,t3));
}
if(t1==3){
t2=read();t3=read();
del(rt,0,1e8,a[t2],t2);
insert(rt,0,1e8,a[t2]=t3,t2);
}
if(t1==4){
t2=read();t3=read();t4=read();
tmp=getk(rt,0,1e8,t2,t3,0,t4-1);
if(tmp==0) puts("-2147483647");
else printf("%d\n",query(rt,0,1e8,tmp,t2,t3));
}
if(t1==5){
t2=read();t3=read();t4=read();
tmp=getk(rt,0,1e8,t2,t3,0,t4);
if(tmp==t3-t2+1) puts("2147483647");
else printf("%d\n",query(rt,0,1e8,tmp+1,t2,t3));
}
}
}

[BZOJ3196] 二逼平衡树 [权值线段树套位置平衡树]的更多相关文章

  1. [bzoj3196][Tyvj1730]二逼平衡树_树套树_位置线段树套非旋转Treap/树状数组套主席树/权值线段树套位置线段树

    二逼平衡树 bzoj-3196 Tyvj-1730 题目大意:请写出一个维护序列的数据结构支持:查询给定权值排名:查询区间k小值:单点修改:查询区间内定值前驱:查询区间内定值后继. 注释:$1\le ...

  2. 【bzoj4605】崂山白花蛇草水 权值线段树套KD-tree

    题目描述 神犇Aleph在SDOI Round2前立了一个flag:如果进了省队,就现场直播喝崂山白花蛇草水.凭借着神犇Aleph的实力,他轻松地进了山东省省队,现在便是他履行诺言的时候了.蒟蒻Bob ...

  3. bzoj4605: 崂山白花蛇草水 权值线段树套KDtree

    bzoj4605: 崂山白花蛇草水 链接 bzoj loj 思路 强制在线,那就权值线段树套KDtree好了,没啥好讲的. KDtree要加平衡因子来重构.另外,那水真难喝. 错误 树套树一边写过了, ...

  4. [BZOJ 3110] [luogu 3332] [ZJOI 2013]k大数查询(权值线段树套线段树)

    [BZOJ 3110] [luogu 3332] [ZJOI 2013]k大数查询(权值线段树套线段树) 题面 原题面有点歧义,不过从样例可以看出来真正的意思 有n个位置,每个位置可以看做一个集合. ...

  5. cogs 1829. [Tyvj 1728]普通平衡树 权值线段树

    1829. [Tyvj 1728]普通平衡树 ★★★   输入文件:phs.in   输出文件:phs.out   简单对比时间限制:1 s   内存限制:1000 MB [题目描述] 您需要写一种数 ...

  6. [BZOJ3600] 没有人的算术 [重量平衡树+权值线段树]

    题面 传送门 思路 这道题目是陈立杰论文<重量平衡树和后缀平衡树在信息学奥赛中的应用 >中关于重量平衡树维护序列排名算法的一个应用 具体方法为:令根节点保存一个实数区间$[0,1]$ 若当 ...

  7. BZOJ3110[Zjoi2013]K大数查询——权值线段树套线段树

    题目描述 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是 ...

  8. 崂山白花蛇草水 权值线段树套KDtree

    Description 神犇Aleph在SDOI Round2前立了一个flag:如果进了省队,就现场直播喝崂山白花蛇草水.凭借着神犇Aleph的实 力,他轻松地进了山东省省队,现在便是他履行诺言的时 ...

  9. BZOJ 3110 ZJOI 2013 K大数查询 树套树(权值线段树套区间线段树)

    题目大意:有一些位置.这些位置上能够放若干个数字. 如今有两种操作. 1.在区间l到r上加入一个数字x 2.求出l到r上的第k大的数字是什么 思路:这样的题一看就是树套树,关键是怎么套,怎么写.(话说 ...

随机推荐

  1. Mac openssl 和curl源码编译

    1.先编译openssl, 下载源码后解压,终端进入源码目录,输入命令配置编译环境:./Configure darwin64-x86_64-cc 等待配置完成后,输入make  和make insta ...

  2. vue登录插件引来的后续问题

    上次介绍了下写的登录弹框插件,过了几天发现点击去注册或者改密码的跳转失效.报错this.$router.push is not a function,继续打印this.$router也是undefin ...

  3. matlab2018a安装后帮助文档打不开解决方法

    安装matlab2018a破解版后,帮助文档提示需要许可证问题(破解版没有可用许可证): 解决方法是把文档设置为离线即可(预设---->帮助---->安装在本地---->小窗口)

  4. C#基础-面向对象-多态

    多态,不同对象对同一方法的不同实现 使用abstract关键字表示抽象类 // 表示是一个抽象类 public abstract class Animal { private string name; ...

  5. nginx+php整合(是让nginx可以运行php,以及下载地址)

    下载地址: nginx:http://nginx.org/en/download.html PHP: https://windows.php.net/download/ 都是官网的自己选择版本 安装文 ...

  6. MySQL Limit 限定查询记录数

    MySQL Limit 限定查询记录数 MySQL LIMIT MySQL 中 LIMIT 关键字用于限定查询记录返回最大数目. 语法: ... LIMIT offset , rows 该语法中,of ...

  7. Codeforces Round #460 (Div. 2).E 费马小定理+中国剩余定理

    E. Congruence Equation time limit per test 3 seconds memory limit per test 256 megabytes input stand ...

  8. 常用 Git 命令清单【转--阮一峰】

    常用 Git 命令清单 感谢作者 --> 原文链接 我每天使用 Git ,但是很多命令记不住. 一般来说,日常使用只要记住下图6个命令,就可以了.但是熟练使用,恐怕要记住60-100个命令. 下 ...

  9. 17 rest-framework框架的基本组件

    序列化 创建一个序列化类 简单使用 开发我们的Web API的第一件事是为我们的Web API提供一种将代码片段实例序列化和反序列化为诸如json之类的表示形式的方式.我们可以通过声明与Django ...

  10. C语言关键词解释

    51单片机关键词 code code的作用是告诉单片机,我定义的数据要放在ROM(程序存储区)里面,写入后就不能再更改