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. C#封装程序集自定义类方法注释提示

    一.为什么使用封装程序集: 在很多分布式应用程序开发中,针对每一种功能可能条用的接口不一样,往往习惯将需要被调用的接口,封装成DLL给调用方应用后使用,这样既规范了调用的方式,又避免了调用出现参数请求 ...

  2. Docker化运维方式讲解

    应用迁移需求 应用运维需要考虑的一个重要问题就是迁移, 在不同机器.机房.环境间迁移.迁移的原因有很多, 比如硬件过保(硬件故障), 机房迁移, 应用扩缩容等. 应用迁移的核心需求是: 简单.迁移操作 ...

  3. box-sizing重置盒子模型计算规则

    目标大纲 一.语法声明 box-sizing : content-box | border-box | inherit 二.属性值说明 content-box 在宽度和高度之外绘制元素的内边距和边框 ...

  4. Synchronization Service Manager

    You can use this UI Shell to check the User Profile log for the SharePoint.   It's stored in this pa ...

  5. Jdk 环境搭建

    在安装完jdk后,还需要对jdk的环境变量进行配置才能正常使用,下面教大家如何配置jdk环境变量: 1.右键选择 计算机→属性→高级系统设置→高级→环境变量

  6. OC KVC

    OC KVC KVC 全称 key valued coding 键值编码 在说KVC之前应该简单的了解一下反射机制 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法. 对于任意 ...

  7. iOS 本地推送通知

    1.什么是本地推送通知 不需要联网的情况下,应用程序经由系统发出的通知 2.本地推送的使用场景 定时提醒,如玩游戏.记账.闹钟.备忘录等 3.实现本地推送通知的步骤 创建本地推送通知的对象UILoca ...

  8. #研发解决方案介绍#Tracing(鹰眼)

    郑昀 最后更新于2014/11/12 关键词:GoogleDapper.分布式跟踪.鹰眼.Tracing.HBase.HDFS. 本文档适用人员:研发   分布式系统为什么需要 Tracing?   ...

  9. Linux LVM学习总结——删除物理卷

    本篇介绍LVM管理中的命令vgreduce, pvremove.其实前面几篇中以及有所涉及. vgreduce:通过删除LVM卷组中的物理卷来减少卷组容量.注意:不能删除LVM卷组中剩余的最后一个物理 ...

  10. SQL周、日、月、年数据统计

    本文只是记录在项目中用到的统计的SQL语句,记一笔以防忘了 /// <summary> /// 获取统计数据 /// </summary> /// <param name ...