题面戳我

您需要写一种数据结构来维护一个有序数列,其中需要提供以下操作:

1、查询k在区间内的排名

2、查询区间内排名为k的值

3、修改某一位值上的数值

4、查询k在区间内的前驱(前驱定义为严格小于x,且最大的数,若不存在输出-2147483647)

5、查询k在区间内的后继(后继定义为严格大于x,且最小的数,若不存在输出2147483647)

时空限制:2s,128M

\(n,m≤5⋅10^4\) 保证有序序列所有值在任何时刻满足\([0,108^]\)

sol

树状数组套动态开节点的权值线段树。

你需要完成以下这一系列操作:

\(Update(i,type)\),把原数组中\(i\)位置上的数进行\(type(=1 or-1)\)操作放到树状数组里面去。类比于树状数组的单点修改操作,这个的复杂度是\(O(\log_{2}^{2}n)\)

\(Sum(l,r,ql,qr)\),统计位置位于\([l,r]\),且权值位于\([ql,qr]\)之间的树的个数。易知我们可以拿\(\log n\)棵线段树跟另外\(\log n\)棵线段树作差。复杂度\(O(\log_{2}^{2}n)\)

\(Rank(l,r,k)\),查找区间\([l,r]\)中第\(k\)大的数是多少。这个就是。。。那个呀。复杂度\(O(\log_{2}^{2}n)\)

我们来看这道题。首先数据离线然后离散化不解释。

操作一,排名就是小于它的数的个数+1。直接调用\(Sum(l,r,1,k-1)\)

操作二,直接调用\(Rank(l,r,k)\)

操作三,两次调用\(Update(pos,type)\),第一次\(type=-1\),在原数组里改掉以后调用\(type=1\)

操作四,前驱。先查\(Sum(l,r,1,k-1)\),若为0则输出-2147483647,否则输出\(Rank(l,r,sum)\)(sum就是前面查的答案)

操作四,后继。同理。先查\(Sum(l,r,1,k)\),若为r-l+1则输出2147483647,否则输出\(Rank(l,r,sum+1)\)

操作四五比较难搞,这里要敲黑板。

然后这题就讲完了。

code

#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 50005;
struct tree{int ls,rs,num;}t[N*200];
struct query{int opt,l,r,pos,k;}q[N];
int n,m,a[N],o[N<<1],len,rt[N],tot,temp[2][20],cnt[2];
int gi()
{
int x=0,w=1;char ch=getchar();
while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if (ch=='-') w=0,ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
void Modify(int &x,int l,int r,int pos,int v)
{
if (!x) x=++tot;
t[x].num+=v;
if (l==r) return;
int mid=l+r>>1;
if (pos<=mid) Modify(t[x].ls,l,mid,pos,v);else Modify(t[x].rs,mid+1,r,pos,v);
}
int Query(int x,int l,int r,int ql,int qr)
{
if (l>=ql&&r<=qr) return t[x].num;
int mid=l+r>>1,s=0;
if (ql<=mid) s+=Query(t[x].ls,l,mid,ql,qr);
if (qr>mid) s+=Query(t[x].rs,mid+1,r,ql,qr);
return s;
}
int Find(int l,int r,int k)
{
if (l==r) return l;
int mid=l+r>>1,sum=0;
for (int i=1;i<=cnt[1];i++) sum+=t[t[temp[1][i]].ls].num;
for (int i=1;i<=cnt[0];i++) sum-=t[t[temp[0][i]].ls].num;
if (k<=sum)
{
for (int i=1;i<=cnt[1];i++) temp[1][i]=t[temp[1][i]].ls;
for (int i=1;i<=cnt[0];i++) temp[0][i]=t[temp[0][i]].ls;
return Find(l,mid,k);
}
else
{
for (int i=1;i<=cnt[1];i++) temp[1][i]=t[temp[1][i]].rs;
for (int i=1;i<=cnt[0];i++) temp[0][i]=t[temp[0][i]].rs;
return Find(mid+1,r,k-sum);
}
}
void Update(int pos,int v)
{
for (int i=pos;i<=n;i+=i&-i)
Modify(rt[i],1,len,a[pos],v);
}
int Sum(int l,int r,int ql,int qr)
{
if (ql>qr) return 0;
int sum=0;
for (int j=r;j;j-=j&-j) sum+=Query(rt[j],1,len,ql,qr);
for (int j=l-1;j;j-=j&-j) sum-=Query(rt[j],1,len,ql,qr);
return sum;
}
int Rank(int l,int r,int k)
{
cnt[1]=cnt[0]=0;
for (int j=r;j;j-=j&-j) temp[1][++cnt[1]]=rt[j];
for (int j=l-1;j;j-=j&-j) temp[0][++cnt[0]]=rt[j];
return o[Find(1,len,k)];
}
int main()
{
n=gi();m=gi();
for (int i=1;i<=n;i++) o[++len]=a[i]=gi();
for (int i=1;i<=m;i++)
{
q[i].opt=gi();
if (q[i].opt!=3) q[i].l=gi(),q[i].r=gi();
else q[i].pos=gi();
q[i].k=gi();
if (q[i].opt!=2) o[++len]=q[i].k;
}
sort(o+1,o+len+1);
len=unique(o+1,o+len+1)-o-1;
for (int i=1;i<=n;i++)
{
a[i]=lower_bound(o+1,o+len+1,a[i])-o;
Update(i,1);
}
for (int i=1;i<=m;i++)
{
if (q[i].opt!=2) q[i].k=lower_bound(o+1,o+len+1,q[i].k)-o;
if (q[i].opt==1)
printf("%d\n",Sum(q[i].l,q[i].r,1,q[i].k-1)+1);
if (q[i].opt==2)
printf("%d\n",Rank(q[i].l,q[i].r,q[i].k));
if (q[i].opt==3)
{
Update(q[i].pos,-1);
a[q[i].pos]=q[i].k;
Update(q[i].pos,1);
}
if (q[i].opt==4)
{
int sum=Sum(q[i].l,q[i].r,1,q[i].k-1);
if (!sum) printf("-2147483647\n");
else printf("%d\n",Rank(q[i].l,q[i].r,sum));
}
if (q[i].opt==5)
{
int sum=Sum(q[i].l,q[i].r,1,q[i].k);
if (sum==q[i].r-q[i].l+1) printf("2147483647\n");
else printf("%d\n",Rank(q[i].l,q[i].r,sum+1));
} }
return 0;
}

[Lugu3380]【模板】二逼平衡树(树套树)的更多相关文章

  1. bzoj 3196 Tyvj 1730 二逼平衡树(线段树套名次树)

    3196: Tyvj 1730 二逼平衡树 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1807  Solved: 772[Submit][Stat ...

  2. bzoj 3196/ Tyvj 1730 二逼平衡树 (线段树套平衡树)

    3196: Tyvj 1730 二逼平衡树 Time Limit: 10 Sec  Memory Limit: 128 MB[Submit][Status][Discuss] Description ...

  3. BZOJ3196 二逼平衡树 ZKW线段树套vector(滑稽)

    我实在是不想再打一遍树状数组套替罪羊树了... 然后在普通平衡树瞎逛的时候找到了以前看过vector题解 于是我想:为啥不把平衡树换成vector呢??? 然后我又去学了一下ZKW线段树 就用ZKW线 ...

  4. BZOJ3196 二逼平衡树 【线段树套平衡树】

    题目 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作: 1.查询k在区间内的排名 2.查询区间内排名为k的值 3.修改某一位值上的数值 4.查询k在区间内的前驱(前驱 ...

  5. BZOJ 3196 Tyvj 1730 二逼平衡树:线段树套splay

    传送门 题意 给你一个长度为 $ n $ 有序数列 $ a $ ,进行 $ m $ 次操作,操作有如下几种: 查询 $ k $ 在区间 $ [l,r] $ 内的排名 查询区间 $ [l,r] $ 内排 ...

  6. [BZOJ3196] [Tyvj1730] 二逼平衡树(线段树 套 Splay)

    传送门 至少BZOJ过了,其他的直接弃. 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作: 1.查询k在区间内的排名 2.查询区间内排名为k的值 3.修改某一位值上的 ...

  7. bzoj 3196 Tyvj 1730 二逼平衡树【线段树 套 splay】

    四舍五入就是个暴力. 对于线段树的每个区间都开一棵按权值排序的splay 对于第二个操作,二分一下,每次查询mid的排名,复杂度 $ O(nlog(n)^{3}) $ 其余的操作都是$ O(nlog( ...

  8. 【BZOJ 3196】二逼平衡树 线段树套splay 模板题

    我写的是线段树套splay,网上很多人写的都是套treap,然而本蒟蒻并不会treap 奉上sth神犇的模板: //bzoj3196 二逼平衡树,支持修改某个点的值,查询区间第k小值,查询区间某个值排 ...

  9. P3380 【模板】二逼平衡树(树套树)(线段树套平衡树)

    P3380 [模板]二逼平衡树(树套树) 前置芝士 P3369 [模板]普通平衡树 线段树套平衡树 这里写的是线段树+splay(不吸氧竟然卡过了) 对线段树的每个节点都维护一颗平衡树 每次把给定区间 ...

  10. 洛谷 P3380 bzoj3196 Tyvj1730 【模板】二逼平衡树(树套树)

    [模板]二逼平衡树(树套树) 题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作: 查询k在区间内的排名 查询区间内排名为k的值 修改某一位值上的数值 查询k在 ...

随机推荐

  1. Centos7安装GitLab

    GitLab CE Download Archives gitlab安装调试小记 Gitlab Free Trial GitLab搭建手记 Gitlab社区版的使用 GUI PNG Gitlab升级到 ...

  2. git命令行工作的正确姿势

    git命令行创建并提交新分支到mater分支的常规步骤 git branch new_branch git status 查看修改的文件 git add changed_files git commi ...

  3. Swift百万线程攻破单例(Singleton)模式

    一.不安全的单例实现 在上一篇文章我们给出了单例的设计模式,直接给出了线程安全的实现方法.单例的实现有多种方法,如下面: class SwiftSingleton { class var shared ...

  4. PHPUnit-附录 A. 断言 (assert)

    [http://www.phpunit.cn/manual/5.7/zh_cn/appendixes.assertions.html] 本附录列举可用的各种断言方法. assertArrayHasKe ...

  5. 织梦默认编辑器 按下回车生成br标签改为生成p标签

    找到文件 \include\ckeditor\config.js 把 config.enterMode = CKEDITOR.ENTER_BR; config.shiftEnterMode = CKE ...

  6. CSS3总结(干货)

    1.css3中好用的选择器 :target //突出显示活动的HTML锚 ::after / ::before{content:" ";} //content必须有,若无内容,用空 ...

  7. C预处理器和C库

    #define #include #undef #ifdef #else #endif #if #elif #else #endif 预处理宏: p463 _ _fun_ _是预定义标识符(函数作用域 ...

  8. js分页功能实现

    实现一个js的分页并在弹出框中显示 1.分页插件使用:bootstarp-paginator.js,需要先引入bootstarp.js和jquery.js等: !function($){"u ...

  9. Java经典编程题50道之四十三

    一个偶数总能表示为两个素数之和. public class Example43 {    public static void main(String[] args) {        f();   ...

  10. InputStream中通过mark和reset方法重复利用缓存

    通过缓存InputStream可重复利用一个InputStream,但是要缓存一整个InputStream内存压力可能是比较大的.如果第一次读取InputStream是用来判断文件流类型,文件编码等用 ...