●赘述题目

对于一个长为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. 201621123050 《Java程序设计》第8周学习总结

    1. 本周学习总结 以你喜欢的方式(思维导图或其他)归纳总结集合相关内容. 2. 书面作业 1. ArrayList代码分析 1.1 解释ArrayList的contains源代码 答:ArrayLi ...

  2. 2018上c语言第0次作业

    随笔: 1.翻阅邹欣老师博客关于师生关系博客,并回答下列问题,每个问题的答案不少于500字: (1)最理想的师生关系是健身教练和学员的关系,在这种师生关系中你期望获得来自老师的哪些帮助? 答:对此问题 ...

  3. 开始 Python 之旅

    开始 Python 之旅 课程来源 本课程基于 Python for you and me 教程翻译制作,其中参考了 Python tutorial 和 The Python Standard Lib ...

  4. EL表达式 与 servlvet3.0的新规范

    EL表达式 EL表达式 是一种简化的数据访问方式,是对jsp脚本的简化  . 如我们在一个页面中需要输出session的保存的一个值: <%  out.println(session.getAt ...

  5. bzoj千题计划288:bzoj1876: [SDOI2009]SuperGCD

    http://www.lydsy.com/JudgeOnline/problem.php?id=1876 高精压位GCD 对于  GCD(a, b)  a>b 若 a 为奇数,b 为偶数,GCD ...

  6. es6+react.js组件入门初探

    React是一个用于构建用户见面的javascript库. React主要用于构建UI,许多人认为React是MVC中的V(视图) React起源于Facebook的内部项目,用来架设Instagra ...

  7. 再一次, 不要使用(include/require)_once

    本文地址: http://www.laruence.com/2012/09/12/2765.html 最近关于apc.include_once_override的去留, 我们做了几次讨论, 这个APC ...

  8. PyMySQL模块的使用

    PyMySQL介绍 PyMySQL是在Python3.x版本中用于连接MySQL服务器的一个库,Python2系列中则使用mysqldb.Django中也可以使用PyMySQL连接MySQL数据库. ...

  9. python Django之文件上传

    python Django之文件上传 使用Django框架进行文件上传共分为俩种方式 一.方式一 通过form表单进行文件上传 #=================================== ...

  10. 文本处理三剑客之grep

    grep grep(支持基本正则表达式),egrep(支持扩展的正则表达式),fgrep(快速的grep,不支持正则表达式) grep是一个最初用于Unix操作系统的命令行工具.在给出文件列表或标准输 ...