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. java 中包的概念,常量,静态与继承

    一.包的概念:创建,使用. 1.新建包: 最上面一行,之前不能再有其它代码了. package 包名; 注意:包名一般都有命名的规范.例如:com.itnba.maya.zy(从大到小). 2.使用包 ...

  2. Java集合概述

    容器,是用来装东西的,在Java里,东西就是对象,而装对象并不是把真正的对象放进去,而是指保存对象的引用.要注意对象的引用和对象的关系,下面的例子说明了对象和对象引用的关系. String str = ...

  3. 超简单的激活Microsoft Office 2016 for Mac 方法

    1.简介: 2016年9月14日更新本博客,激活工具同样适用于Office 15.25(160817)版本.我此前在国外网站上找到一个App,下载之后运行,直接点击一个黑色开锁的标识按钮,输入系统密码 ...

  4. JavaScript高阶函数

    所谓高阶函数(higher-order function) 就是操作函数的函数,它接收一个或多个函数作为参数,并返回一个新函数. 下面的例子接收两个函数f()和g(),并返回一个新的函数用以计算f(g ...

  5. JavaScript类属性

    对象的类属性(class attribute)是一个字符串,用以表示对象的类型信息.ECMAScript3和ECMAScript5都未提供设置这个属性的方法,并只有一个间接的方法可以查询它.默认的to ...

  6. 前端学习笔记——移动前端UI选择

    一.jQuery Mobile jQuery Mobile 是jQuery在移动设备上的版本,做为主要针对移动设备的框架来说,它提供一个移动设备平台统一的接口来兼容不同的移动平台,其特性包括: 1.简 ...

  7. 鼠标滑过图片变暗文字链接滑出jQuery特效

    效果体验:http://hovertree.com/texiao/jquery/7.htm HTML文件代码: <!DOCTYPE html> <html xmlns="h ...

  8. getElementsByTagName() 方法

    HTML DOM Document 对象 定义和用法 getElementsByTagName() 方法可返回带有指定标签名的对象的集合. 语法 document.getElementsByTagNa ...

  9. iOS UIMenuController菜单

    //1:普通 ////  ViewController.m//  DemoTest#import "ViewController.h"@interface ViewControll ...

  10. 原生JS 表单提交验证器

    转载:http://www.cnblogs.com/sicd/p/4613628.html 一.前言 最近在开发一个新项目,需要做登陆等一系列的表单提交页面.在经过“缜密”的讨论后,我们决定 不用外部 ...