●赘述题目

对于一个长为n(n<50000)的序列(序列中的数小于1000000000),现有如下两种指令:

Q a b c:询问区间[a,b]中第c小的数。

C p b:将序列中的从左往右数第p个数改成b。

●题解

(整体二分应该可以做吧。。。但写不来了)

主席树+树状数组套线段树维护。

本题和POJ 2104 K-th Number相比,多了一个修改操作,但真的做得我心累。

看看POJ 2104 的查询函数:

查询区间到底是往左还是往右,这决于tmp与k大小关系。但本题因为有修改操作,导致上图的sum[ ]存的信息不正确,无法正确二分下去。所以我们需要就修改操作进行信息的更新。

对于一个修改操作C p b,我们可以发现,这个操作会影响tr[p-n]这一堆主席树,那当然是不能直接枚举这一堆主席树,挨个进行修改,显然会超时。

于是便尝试再另外弄一个东西来单独维护修改后的信息。

不难发现,这一堆主席树,它们的修改操作是一模一样的,那便可以看作是一个区间修改,单点查询(如下图)呢,那我们就用树状数组来维护。

squery(x)是对树状数组的询问,表示序列区间[1-x]内有多少在[l-r](权值)范围内的数发生了变化(少了则减,多了则加)。

举个例子,当原序列为 1 2 3 4 ,已经执行了修改操作C 2 5

若 l=1,r=4:squery(1)=0; squery(2)=-1; squery(3)=-1; squery(4)=-1;

若 l=5,r=8 : squery(1)=0; squery(2)=1; squery(3)=1; squery(4)=1;

若 l=1,r=8 : squery(1)=0; squery(2)=0; squery(3)=0; squery(4)=0;

(一定要弄懂哦。)

另外,树状数组该如何维护在[l-r]范围内的数发生的变化呢,那就树套树呗(以前从未写过树套树。。。),对于每个树状数组的节点建一颗权值线段树。

○至此,便有了一个大致的修改操作的思路:

对于C p b ,

先是枚举树状数组的节点(数组数组区间修改(单点查询),不用多说了吧)

for(int i=p;i<=n;i+=lowbit(i)) xmodify( ) ,对枚举到的节点里套的权值线段树进行单点修改。

到时候查询树状数组的时候,就for(int i=p;i>0;i-=lowbit(i)) ret+=xquery( ),对每个枚举到的节点里套的权值线段树进行权值区间查询并累加就好了。

(注意:若每个树状数组节点里都套的是一棵完整的权值线段树,空间必然不够,但因为修改数不超过10000,每次修改都只修改log n条链,这意味着我们需要用到的权值线段树的某些位置,在修改时临时建就好了,最后每个树状数组节点里都套的都是我们想象的完整的权值线段树,实际上只是几条链,甚至一条链没有。)

那么,完了吗?

我们算一算: m个操作,每个操作有一个log级别的主席树查询,再有一个 log级别的树状数组查询, 再套一个log级别的权值线段树查询,总的是复杂度是mlogloglog,可能要超时呢。

看看别的大佬的做法,每次主席树查询到[l-r]区间时,我们查询的每个权值线段树区间也是[l-r](且该区间是直接二分得到的,而不是几个小区间拼凑而来),那便可以先用一个数组存下要用的权值线段树的节点,当询问树状数组时由储存的权值线段树的节点直接获取权值区间信息便是了。这样是一个 mloglog的复杂度。

所以,这里生长着两棵树,一颗是主席树(保存初始信息),一颗是套了线段树的树状数组(维护修改信息)。那么,本题也就结束了。

好吧,其实还有漫长的调试查错呢!

●代码

先大致解释一下函数名:(XX表示原函数名,如build,modify,squery。。。)

zXX表示关于主席树的函数;

sXX表示关于树状数组的函数;

xXX表示关于线段树的函数;

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int MAXN = 60010;
const int M = 2500010;
using namespace std;
int sum[M],ls[M],rs[M];
int xx[MAXN],aa[MAXN],tr[MAXN],s[MAXN],use[MAXN];
int tot=0,cnt,n,m,pa,pb;
struct operation{
char ch;int a,b,c;
}op[10005];
int discrete(int x){return lower_bound(xx+1,xx+cnt+1,x)-xx;}
void xmodify(int &u,int l,int r,int x,int d)
{
if(!u) u=++tot,sum[u]=0;
sum[u]+=d;
if(l==r){return;}
int mid=(l+r)>>1;
if(x<=mid) xmodify(ls[u],l,mid,x,d);
else xmodify(rs[u],mid+1,r,x,d);
}
int lowbit(int x) {return x&-x;}
void smodify(int p,int x,int d)
{
for(int i=p;i<=n;i+=lowbit(i)) xmodify(s[i],1,cnt,x,d);
}
int squery(int x)
{
int ret=0;
for(int i=x;i>0;i-=lowbit(i)) ret+=sum[ls[use[i]]];
return ret;
}
void zbuild(int &u,int l,int r)
{
u=++tot; sum[u]=0;
if(l==r) return;
int mid=(l+r)>>1;
zbuild(ls[u],l,mid);
zbuild(rs[u],mid+1,r);
}
void zupdate(int &u,int last,int l,int r,int p)
{
u=++tot; sum[u]=sum[last]+1;
if(l==r) return;
ls[u]=ls[last]; rs[u]=rs[last];
int mid=(l+r)>>1;
if(p<=mid) zupdate(ls[u],ls[last],l,mid,p);
else zupdate(rs[u],rs[last],mid+1,r,p);
}
int zquery(int a,int b,int l,int r,int k)
{
if(l==r) return l;
int mid=(l+r)>>1;
int tmp=squery(pb)-squery(pa)+sum[ls[b]]-sum[ls[a]];
if(tmp>=k)
{
for(int i=pa;i>0;i-=lowbit(i)) use[i]=ls[use[i]];
for(int i=pb;i>0;i-=lowbit(i)) use[i]=ls[use[i]];
return zquery(ls[a],ls[b],l,mid,k);
}
else
{
for(int i=pa;i>0;i-=lowbit(i)) use[i]=rs[use[i]];
for(int i=pb;i>0;i-=lowbit(i)) use[i]=rs[use[i]];
return zquery(rs[a],rs[b],mid+1,r,k-tmp);
}
}
int main()
{
int ans,dd,T;
scanf("%d",&T);
while(T--)
{
tot=cnt=dd=0;
scanf("%d%d",&n,&m);
memset(s,0,sizeof(s));
memset(ls,0,sizeof(ls));
memset(rs,0,sizeof(rs));
for(int i=1;i<=n;i++) scanf("%d",&aa[i]),xx[++dd]=aa[i];
for(int i=1;i<=m;i++)
{
scanf(" %c",&op[i].ch);
if(op[i].ch=='Q') scanf("%d%d%d",&op[i].a,&op[i].b,&op[i].c);
else scanf("%d%d",&op[i].a,&op[i].b),xx[++dd]=op[i].b;
}
sort(xx+1,xx+dd+1);
cnt=unique(xx+1,xx+dd+1)-xx-1;
zbuild(tr[0],1,cnt);
for(int i=1;i<=n;i++)
{
int p=discrete(aa[i]);
zupdate(tr[i],tr[i-1],1,cnt,p);
}
for(int i=1,a,b,k;i<=m;i++)
{
a=op[i].a; b=op[i].b;
if(op[i].ch=='Q')
{
k=op[i].c;
pa=a-1; pb=b;
for(int j=pa;j>0;j-=lowbit(j)) use[j]=s[j];
for(int j=pb;j>0;j-=lowbit(j)) use[j]=s[j];
ans=zquery(tr[a-1],tr[b],1,cnt,k);
printf("%d\n",xx[ans]);
}
else
{
int x=discrete(aa[a]),y=discrete(b);
smodify(a,x,-1); smodify(a,y,1);
aa[a]=b;
}
}
}
return 0;
}

●ZOJ 2112 Dynamic Rankings的更多相关文章

  1. ZOJ 2112 Dynamic Rankings(动态区间第 k 大+块状链表)

    题目大意 给定一个数列,编号从 1 到 n,现在有 m 个操作,操作分两类: 1. 修改数列中某个位置的数的值为 val 2. 询问 [L, R] 这个区间中第 k 大的是多少 n<=50,00 ...

  2. 主席树[可持久化线段树](hdu 2665 Kth number、SP 10628 Count on a tree、ZOJ 2112 Dynamic Rankings、codeforces 813E Army Creation、codeforces960F:Pathwalks )

    在今天三黑(恶意评分刷上去的那种)两紫的智推中,突然出现了P3834 [模板]可持久化线段树 1(主席树)就突然有了不详的预感2333 果然...然后我gg了!被大佬虐了! hdu 2665 Kth ...

  3. 整体二分(SP3946 K-th Number ZOJ 2112 Dynamic Rankings)

    SP3946 K-th Number (/2和>>1不一样!!) #include <algorithm> #include <bitset> #include & ...

  4. 整体二分&cdq分治 ZOJ 2112 Dynamic Rankings

    题目:单点更新查询区间第k大 按照主席树的思想,要主席树套树状数组.即按照每个节点建立主席树,然后利用树状数组的方法来更新维护前缀和.然而,这样的做法在实际中并不能AC,原因即卡空间. 因此我们采用一 ...

  5. ZOJ 2112 Dynamic Rankings(主席树の动态kth)

    题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2112 The Company Dynamic Rankings ...

  6. ZOJ 2112 Dynamic Rankings(带修改的区间第K大,分块+二分搜索+二分答案)

    Dynamic Rankings Time Limit: 10 Seconds      Memory Limit: 32768 KB The Company Dynamic Rankings has ...

  7. ZOJ -2112 Dynamic Rankings 主席树 待修改的区间第K大

    Dynamic Rankings 带修改的区间第K大其实就是先和静态区间第K大的操作一样.先建立一颗主席树, 然后再在树状数组的每一个节点开线段树(其实也是主席树,共用节点), 每次修改的时候都按照树 ...

  8. zoj 2112 Dynamic Rankings 动态第k大 线段树套Treap

    Dynamic Rankings Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.zju.edu.cn/onlinejudge/show ...

  9. 高级数据结构(树状数组套主席树):ZOJ 2112 Dynamic Rankings

    Dynamic Rankings Time Limit: 10 Seconds      Memory Limit: 32768 KB The Company Dynamic Rankings has ...

  10. ZOJ 2112 Dynamic Rankings (动态第 K 大)(树状数组套主席树)

    Dynamic Rankings Time Limit: 10 Seconds      Memory Limit: 32768 KB The Company Dynamic Rankings has ...

随机推荐

  1. 201621123060《JAVA程序设计》第九周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结集合与泛型相关内容. 2. 书面作业 本次作业题集集合 1. List中指定元素的删除(题集题目) 1.1 实验总结.并回答:列举至 ...

  2. 201621123044《JAVA程序设计》第一周学习总结

    1. 本周学习总结 1.了解了JAVA的诞生以及发展历史简介.JAVA语言的特点,以及JAVA的电脑安装以及环境配置. 2.JAVA不仅可以用eclipse进行编写,也可以在记事本和notepad++ ...

  3. IE bug:ajax请求返回304解决方案

    bug说明: 同一账户下的默认收货地址只有一个,默认收货地址可以修改,修改完成后,使用ajax重新加载收货地址部分. 默认收货地址状态标记:status = 1: 在IE浏览器做了修改后,重新加载的数 ...

  4. WPS怎么让前几页的页眉或者页脚与后面的不同

    其实不管利用WPS还是office对文档还是PPT进行操作,其实核心思想还是一种编程,主要是前端的编程,就是通过改变一些这些软件设置的样式,然后通过改变这些样式,使这些文字以老师要求的格式显示出来的, ...

  5. phalcon框架命名空间

    命名空间第一影像就是实际上就相当宏定义,就是需要把一个很长的带有路径的类文件指定一个空间,然后就可直接用简单简写模式 当然如果是外部文件需要首先引入外部文件,如果不引入外部文件还是会报错.一般最会出错 ...

  6. Linux入门(1)_VMware和系统分区和系统安装和远程登陆管理

    1 VMware的安装和使用 注意有 快照 和 克隆 的功能. 快照相当于建立一个 系统还原点, 可以随时恢复到原来状态. 克隆功能可以复制一个和当前一样的系统,并可以选择链接安装,只使用很少的空间就 ...

  7. js控制表格实时编辑

    点击添加,在表格的最后一行添加一行表单元素,右侧按钮变为保存和取消.(点击保存,数据用ajax无刷新添加到界面,点击取消,取消此行的添加.)点击编辑,在本行改为表单,带有原来的值,右侧按钮变为确认和取 ...

  8. windbg查找Kernel32.dll基址

    一.首先准备好一个程序,运行起来,用windbg进行附加调试,由于每个windows下的程序都会加载kernel32.dll,因此,找基址的过程是一样的:  二.查看PEB地址: 法一.r $peb ...

  9. OAuth2.0学习(1-6)授权方式3-密码模式(Resource Owner Password Credentials Grant)

    授权方式3-密码模式(Resource Owner Password Credentials Grant) 密码模式(Resource Owner Password Credentials Grant ...

  10. django关闭调试信息,打开内置错误视图

    1 内置错误视图 Django内置处理HTTP错误的视图,主要错误及视图包括: 404错误:page not found视图 500错误:server error视图 400错误:bad reques ...