Dynamic Rankings


Time Limit: 10 Seconds      Memory Limit: 32768 KB

The Company Dynamic Rankings has developed a new kind of computer that is no longer satisfied with the query like to simply find the k-th smallest number of the given N numbers. They have developed a more powerful system such that for N numbers a[1], a[2], ..., a[N], you can ask it like: what is the k-th smallest number of a[i], a[i+1], ..., a[j]? (For some i<=j, 0<k<=j+1-i that you have given to it). More powerful, you can even change the value of some a[i], and continue to query, all the same.

Your task is to write a program for this computer, which

- Reads N numbers from the input (1 <= N <= 50,000)

- Processes M instructions of the input (1 <= M <= 10,000). These instructions
include querying the k-th smallest number of a[i], a[i+1], ..., a[j] and change
some a[i] to t.

Input

The first line of the input is a single number X (0 < X <= 4), the number
of the test cases of the input. Then X blocks each represent a single test case.

The first line of each block contains two integers N and M, representing N numbers
and M instruction. It is followed by N lines. The (i+1)-th line represents the
number a[i]. Then M lines that is in the following format

Q i j k or
C i t

It represents to query the k-th number of a[i], a[i+1], ..., a[j] and change
some a[i] to t, respectively. It is guaranteed that at any time of the operation.
Any number a[i] is a non-negative integer that is less than 1,000,000,000.

There're NO breakline between two continuous test cases.

Output

For each querying operation, output one integer to represent the result. (i.e.
the k-th smallest number of a[i], a[i+1],..., a[j])

There're NO breakline between two continuous test cases.

Sample Input


Q
C
Q Q
C
Q

Sample Output


Solution

算是整体二分的经典题目吧

大概是带修改的求区间第k小

这里用离线做法

首先,在数据范围较大的情况下要对于所有出现在输入中的区间数字进行离散化

之后,存下所有的询问,将修改视为2操作,赋值操作视为1操作,3操作则是询问

将离散化后的数字区间拿来做二分

大致思路是这样的:

1.确定答案范围[L,R],mid=L+R>>1;
2.算出答案在[L,mid]内的操作对答案在[mid+1,R]内的操作的贡献;
3.将答案在[L,mid]内的操作放入队列Q1,solve(Q1,L,mid)
将答案在[mid+1,R]内的操作放入Q2,solve(Q2,mid+1,R)

由于询问和操作是按时间顺序处理的,为了满足二分性质还需对询问和操作的区间进行排序,这里用到一点树状数组求逆序对的思想,我们用树状数组维护题目中虚拟的那个区间中的n个数的区间,用树状数组统计区间中询问和操作的贡献,用差分进行区间修改。

之后对每个询问和操作进行分治,分到两个区间中分别处理

每当遇到当前区间已经缩小成为点时,求记录答案

最后按要求输出即可

#include<map>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int M=;
inline int read(){
int x=,c=getchar(),f=;
for(;c<||c>;c=getchar())
if(!(c^))
f=-;
for(;c>&&c<;c=getchar())
x=(x<<)+(x<<)+c-;
return x*f;
}
vector<int>v;
map<int,int>h;
int Qcnt,Pcnt,a[M],ans[M],c[M],n,tmp[M];
struct Que{
int x,y,typ,cur,kth,pos;
}q[M],q1[M],q2[M];
inline void add(int x,int d){
for(;x<=n;x+=x&(-x))
c[x]+=d;
}
inline int sum(int x){
int res=;
for(;x;x-=x&(-x))
res+=c[x];
return res;
}
void divide(int hd,int tl,int l,int r){
if(hd>tl)
return;
if(l==r){
for(int i=hd;i<=tl;i++)
if(q[i].typ==)
ans[q[i].pos]=l;
return;
}
int mid=l+r>>;
for(int i=hd;i<=tl;i++){
if(q[i].typ==)
tmp[i]=sum(q[i].y)-sum(q[i].x-);
else
if(q[i].typ==&&q[i].y<=mid)
add(q[i].x,-);
else
if(q[i].typ==&&q[i].y<=mid)
add(q[i].x,);
}
for(int i=hd;i<=tl;i++){
if(q[i].typ==&&q[i].y<=mid)
add(q[i].x,);
else
if(q[i].typ==&&q[i].y<=mid)
add(q[i].x,-);
}
int l1=,l2=;
for(int i=hd;i<=tl;i++){
if(q[i].typ==){
if(q[i].cur+tmp[i]>=q[i].kth)
q1[++l1]=q[i];
else{
q[i].cur+=tmp[i];
q2[++l2]=q[i];
}
}
else{
if(q[i].y<=mid)
q1[++l1]=q[i];
else
q2[++l2]=q[i];
}
}
for(int i=;i<=l1;i++)
q[hd+i-]=q1[i];
for(int i=;i<=l2;i++)
q[hd+l1+i-]=q2[i];
divide(hd,hd+l1-,l,mid);
divide(hd+l1,tl,mid+,r);
}
int main(){
int m,T;
T=read();
while(T--){
Qcnt=Pcnt=;
v.clear();
h.clear();
memset(a,,sizeof(a));
memset(c,,sizeof(c));
memset(tmp,,sizeof(tmp));
n=read(),m=read();
for(int i=;i<=n;i++){
a[i]=read();
q[++Qcnt].x=i;
q[Qcnt].y=a[i];
q[Qcnt].typ=;
v.push_back(a[i]);
}
for(int i=;i<=m;i++){
char sign[];
int x,y,z;
scanf("%s",sign);
if(sign[]=='Q'){
x=read(),y=read(),z=read();
q[++Qcnt].x=x;
q[Qcnt].y=y;
q[Qcnt].kth=z;
q[Qcnt].typ=;
q[Qcnt].cur=;
q[Qcnt].pos=++Pcnt;
}
else{
x=read(),y=read();
q[++Qcnt].x=x;
q[Qcnt].y=a[x];
q[Qcnt].typ=;
q[++Qcnt].x=x;
q[Qcnt].y=y;
q[Qcnt].typ=;
a[x]=y;
v.push_back(y);
}
}
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
for(int i=;i<v.size();i++)
h[v[i]]=i+;
for(int i=;i<=Qcnt;i++)
if(q[i].typ<=)
q[i].y=h[q[i].y];
divide(,Qcnt,,v.size());
for(int i=;i<=Pcnt;i++)
printf("%d\n",v[ans[i]-]);
}
return ;
}

其实,我还写了可持久化线段树的写法,只是因为空间不够溢出了,返回Segment Fault,但程序本身没有什么错误

#include <stdio.h>
#include <string.h>
#define mid (l + r >> 1) const int MAXN = 5e4 + ;
const int INF = 1e9 + ; int Rin(){
int x = , c = getchar(), f = ;
for(; c < || c > ; c = getchar())if(!(c ^ ))f = -;
for(; c > && c < ; c = getchar())x = (x << ) + (x << ) + c - ;
return x * f;
} int tot, n, m, a, b, v[MAXN], L[MAXN * ], R[MAXN * ], rt[MAXN * ], ls[MAXN * ], rs[MAXN * ], sum[MAXN * ]; void Modify(int &pr, int pl, int l, int r, int v, int d){
pr = ++tot; sum[pr] = sum[pl] + d; ls[pr] = ls[pl]; rs[pr] = rs[pl];
if(l + < r)
v < mid ? Modify(ls[pr], ls[pl], l, mid, v, d) : Modify(rs[pr], rs[pl], mid, r, v, d);
} int Query(int l, int r, int k){
if(l + == r)return l;
int suml = , sumr = ;
for(int i = ; i <= a; i++)
suml += sum[ls[L[i]]];
for(int i = ; i <= b; i++)
sumr += sum[ls[R[i]]];
if(sumr - suml >= k){
for(int i = ; i <= a; i++)
L[i] = ls[L[i]];
for(int i = ; i <= b; i++)
R[i] = ls[R[i]];
return Query(l, mid, k);
}
for(int i = ; i <= a; i++)
L[i] = rs[L[i]];
for(int i = ; i <= b; i++)
R[i] = rs[R[i]];
return Query(mid, r, k - sumr + suml);
} int main(){
int x, y, k, T = Rin(); char sign[];
while(T--){
tot = ;
memset(v, , sizeof(v));
memset(rt, , sizeof(rt));
memset(ls, , sizeof(ls));
memset(rs, , sizeof(rs));
memset(L, , sizeof(L));
memset(R, , sizeof(R));
memset(sum, , sizeof(sum));
n = Rin(), m = Rin();
for(int i = ; i <= n; i++){
v[i] = Rin();
for(int j = i; j <= n; j += j & (-j))
Modify(rt[j], rt[j], , INF, v[i], );
}
while(m--){
scanf("%s", sign);
if(sign[] == 'Q'){
a = b = ; x = Rin() - , y = Rin(), k = Rin();
for(int j = x; j; j -= j & (-j))
L[++a] = rt[j];
for(int j = y; j; j -= j & (-j))
R[++b] = rt[j];
printf("%d\n", Query(, INF, k));
}
else{
x = Rin(), y = Rin();
for(int j = x; j <= n; j += j & (-j))
Modify(rt[j], rt[j], , INF, v[x], -);
v[x] = y;
for(int j = x; j <= n; j += j & (-j))
Modify(rt[j], rt[j], , INF, v[x], );
}
}
}
return ;
}

[bzoj1901][zoj2112][Dynamic Rankings] (整体二分+树状数组 or 动态开点线段树 or 主席树)的更多相关文章

  1. BZOJ1901: Zju2112 Dynamic Rankings(整体二分 树状数组)

    Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 9094  Solved: 3808[Submit][Status][Discuss] Descript ...

  2. 【BZOJ1901】Dynamic Rankings [整体二分]

    Dynamic Rankings Time Limit: 10 Sec  Memory Limit: 128 MB[Submit][Status][Discuss] Description 给定一个含 ...

  3. ZOJ2112 Dynamic Rankings(整体二分)

    今天学习了一个奇技淫巧--整体二分.关于整体二分的一些理论性的东西,可以参见XRH的<浅谈数据结构题的几个非经典解法>.然后下面是一些个人的心得体会吧,写下来希望加深一下自己的理解,或者如 ...

  4. BZOJ 1901 Dynamic Rankings (整体二分+树状数组)

    题目大意:略 洛谷传送门 这道题在洛谷上数据比较强 貌似这个题比较常见的写法是树状数组套主席树,动态修改 我写的是整体二分 一开始的序列全都视为插入 对于修改操作,把它拆分成插入和删除两个操作 像$C ...

  5. BZOJ 1901 Zju2112 Dynamic Rankings ——整体二分

    [题目分析] 上次用树状数组套主席树做的,这次用整体二分去水. 把所有的查询的结果一起进行二分,思路很好. [代码] #include <cstdio> #include <cstr ...

  6. BZOJ.1901.Dynamic Rankings(整体二分)

    题目链接 BZOJ 洛谷 (以下是口胡) 对于多组的询问.修改,我们可以发现: 假设有对p1,p2,p3...的询问,在这之前有对p0的修改(比如+1),且p0<=p1,p2,p3...,那么我 ...

  7. 【BZOJ3295】【块状链表+树状数组】动态逆序对

    Description 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计 ...

  8. BZOJ4785 ZJOI2017树状数组(概率+二维线段树)

    可以发现这个写挂的树状数组求的是后缀和.find(r)-find(l-1)在模2意义下实际上查询的是l-1~r-1的和,而本来要查询的是l~r的和.也就是说,若结果正确,则a[l-1]=a[r](mo ...

  9. 【树链剖分】【树状数组】【最近公共祖先】【块状树】bzoj3631 [JLOI2014]松鼠的新家

    裸题,树状数组区间修改+单点查询.当然要稍微讨论一下链的左右端点是否修改的情况咯. #include<cstdio> #include<algorithm> #include& ...

随机推荐

  1. 用Spring Boot颠覆Java应用开发

    Java开发概述: 使用Java做Web应用开发已经有近20年的历史了,从最初的Servlet1.0一步步演化到现在如此多的框架,库以及整个生态系统.经过这么长时间的发展,Java作为一个成熟的语言, ...

  2. HTML5学习笔记

    参考资料:http://www.runoob.com/html/html-tutorial.html 1.html5声明.将此html文档标记为html5文档 <!DOCTYPE html> ...

  3. Python默认版本修改

    Python默认版本修改 当电脑安装了多个版本的Python,而Shell中默认的Python不是你想要的,这个时候就需要对Python的默认版本进行修改. 在Windows中,可以通过修改环境变量的 ...

  4. [moka同学笔记]五、Yii2.0课程笔记(魏曦老师教程)[审核功能]

  5. 大公司c#&.net转型java的原因有哪些?

    历来就听说有编程语言“鄙视链”的说法,而如今月经贴上的那些事儿,还真让我给遇到了. 以下内容来自知乎,纯属扯淡,易引发口水战,看完勿人身攻击. 目的给盲目的公司决策者.开发人员科普下,有个客观清醒的认 ...

  6. rabbitmq性能优化之Consumer utilisation

    如下所示,每个rabbitmq队列除了发布和消费吞吐量外,还有一个评价MQ队列效率的更加重要的指标Consumer utilisation ,如下: 在最佳利用率情况下,这个值能够达到100%,并且生 ...

  7. MySQL学习笔记 -- 数据表的基本操作

    数据库是一个可以存放数据库对象的容器,数据库对象包括:表.视图.存储过程.函数.触发器.事件.其中,表是数据库最基本的元素,是其他数据库对象的前提条件. 表中的一列称为一个字段,一行称为一条记录. 1 ...

  8. iOS之2016面试题二

    前言 招聘高峰期来了,大家都非常积极地准备着跳槽,那么去一家公司面试就会有一堆新鲜的问题,可能不会,也可能会,但是了解不够深.本篇文章为群里的小伙伴们去要出发公司的笔试题,由笔者整理并提供笔者个人参考 ...

  9. 初学HTML 表单交互标签

    表单标签在网站中主要负责的是进行与用户间的交互, 如果没有了交互, 那么网站就只是一个展示, 会死气沉沉的. <form>表单标签 <form>表单标签可以把浏览者(也就是我们 ...

  10. iframe大小自适应

    前几天,舍友去某互联网公司面前端研发工程师.回来后,他就跟我们聊了下面试官给他出的题.其中,有一道题是“如何实现iframe高度的自适应?”.好吧,我承认,我听到iframe这个词的第一反应就是:这个 ...