[bzoj1901][zoj2112][Dynamic Rankings] (整体二分+树状数组 or 动态开点线段树 or 主席树)
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 主席树)的更多相关文章
- BZOJ1901: Zju2112 Dynamic Rankings(整体二分 树状数组)
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 9094 Solved: 3808[Submit][Status][Discuss] Descript ...
- 【BZOJ1901】Dynamic Rankings [整体二分]
Dynamic Rankings Time Limit: 10 Sec Memory Limit: 128 MB[Submit][Status][Discuss] Description 给定一个含 ...
- ZOJ2112 Dynamic Rankings(整体二分)
今天学习了一个奇技淫巧--整体二分.关于整体二分的一些理论性的东西,可以参见XRH的<浅谈数据结构题的几个非经典解法>.然后下面是一些个人的心得体会吧,写下来希望加深一下自己的理解,或者如 ...
- BZOJ 1901 Dynamic Rankings (整体二分+树状数组)
题目大意:略 洛谷传送门 这道题在洛谷上数据比较强 貌似这个题比较常见的写法是树状数组套主席树,动态修改 我写的是整体二分 一开始的序列全都视为插入 对于修改操作,把它拆分成插入和删除两个操作 像$C ...
- BZOJ 1901 Zju2112 Dynamic Rankings ——整体二分
[题目分析] 上次用树状数组套主席树做的,这次用整体二分去水. 把所有的查询的结果一起进行二分,思路很好. [代码] #include <cstdio> #include <cstr ...
- BZOJ.1901.Dynamic Rankings(整体二分)
题目链接 BZOJ 洛谷 (以下是口胡) 对于多组的询问.修改,我们可以发现: 假设有对p1,p2,p3...的询问,在这之前有对p0的修改(比如+1),且p0<=p1,p2,p3...,那么我 ...
- 【BZOJ3295】【块状链表+树状数组】动态逆序对
Description 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计 ...
- BZOJ4785 ZJOI2017树状数组(概率+二维线段树)
可以发现这个写挂的树状数组求的是后缀和.find(r)-find(l-1)在模2意义下实际上查询的是l-1~r-1的和,而本来要查询的是l~r的和.也就是说,若结果正确,则a[l-1]=a[r](mo ...
- 【树链剖分】【树状数组】【最近公共祖先】【块状树】bzoj3631 [JLOI2014]松鼠的新家
裸题,树状数组区间修改+单点查询.当然要稍微讨论一下链的左右端点是否修改的情况咯. #include<cstdio> #include<algorithm> #include& ...
随机推荐
- linux下c程序的链接、装载和库(3)
9. 目标文件放在一起-->静态库. 你的同事给出的目标文件太多了,从 one.o two.o …… …… 一直到 xxx.o. 好的,你如果真正想用,你的同事提供的这些现有的目标文件,你得做三 ...
- 一个过滤特殊字符的JS
<script language="javascript"> function checkForms() { var iu, iuu, regArray=new Arr ...
- [翻译]写给精明Java开发者的测试技巧
我们都会为我们的代码编写测试,不是吗?毫无疑问,我知道这个问题的答案可能会从 “当然,但你知道怎样才能避免写测试吗?” 到 “必须的!我爱测试”都有.接下来我会给你几个小建议,它们可以让你编写测试变得 ...
- 【HTML5&CSS3进阶学习01】气泡组件的实现
前言 气泡组件在实际工作中非常普遍,无论是网页中还是app中,比如: 我们这里所谓气泡组件是指列表型气泡组件,这里就其dom实现,css实现,js实现做一个讨论,最后对一些细节点做一些说明,希望对各位 ...
- angular源码分析:angular中脏活累活的承担者之$interpolate
一.首先抛出两个问题 问题一:在angular中我们绑定数据最基本的方式是用两个大括号将$scope的变量包裹起来,那么如果想将大括号换成其他什么符号,比如换成[{与}],可不可以呢,如果可以在哪里配 ...
- DevExpress使用的过期版本解决方法
Windows控件确实有点丑,一般刚学习VS编程或者临时创建测试程序时才会使用:第三方控件,增强了一些功能,美化了控件,现在比较流行:再高级一点,就是使用WPF,看效果还不错,估计以后会成为C/S的主 ...
- How to get Timer Job History
1. Get Timer Job internal name with id. Job ID can be found in SharePoint CA. Below PowerShell can h ...
- iOS开发--引用计数与ARC
以下是关于内存管理的学习笔记:引用计数与ARC. iOS5以前自动引用计数(ARC)是在MacOS X 10.7与iOS 5中引入一项新技术,用于代替之前的手工引用计数MRC(Manual Refer ...
- 【代码笔记】iOS-文字走马灯效果
一,效果图. 二,工程图. 三,代码. RootViewController.h #import <UIKit/UIKit.h> @interface RootViewController ...
- 初识JAVA之OOP
有一段时间没发博客了,每次手打还是很累,但感觉很充实.. 最近发现很多初学者到了面向对象编程这个知识点时,不太清楚类是如何转化成为对象的,很是困扰,今天我在这里谈谈我的理解,大家一起来研究学习... ...