您的位置:首页 > 其它

【ZOJ2112】【整体二分+树状数组】带修改区间第k大

2015-03-15 15:12 495 查看
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
, 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

2

5 3

3 2 1 4 7

Q 1 4 3

C 2 6

Q 2 5 3

5 3

3 2 1 4 7

Q 1 4 3

C 2 6

Q 2 5 3

Sample Output

3

6

3

6

【分析】

裸题,不说了。

按照这种方法的话,离线的带插入修改区间第K大也应该可以做了。

不过这题的经典作法是树状数组上套可持久化线段树,不过这样空间消耗会很大。

可能要用动态开点?

转一个用块状链表的:http://www.cnblogs.com/zhj5chengfeng/archive/2013/08/19/3268162.html

/*
宋代晏殊
《蝶恋花·槛菊愁烟兰泣露》

槛菊愁烟兰泣露。罗幕轻寒,燕子双飞去。明月不谙离恨苦。斜光到晓穿朱户。
昨夜西风凋碧树。独上高楼,望尽天涯路。欲寄彩笺兼尺素。山长水阔知何处。
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <utility>
#include <iomanip>
#include <string>
#include <cmath>
#include <queue>
#include <assert.h>
#include <map>
#include <ctime>
#include <cstdlib>
#include <stack>
#define LOCAL
const int INF = 1000000000;
const int MAXN = 300000 + 10;
using namespace std;
struct QUERY{
int x, y;
int k, s, type, cur;//cur用来记录前面的值
}q[MAXN], q1[MAXN], q2[MAXN];
int Ans[MAXN];
int tmp[MAXN], c[MAXN];
int n, m, num, cnt;
int data[MAXN];

inline int lowbit(int x){return x&-x;}
void add(int x, int val){
while (x <= n){
c[x] += val;
x += lowbit(x);
}
return;
}
int sum(int x){
int cnt = 0;
while (x > 0){
cnt += c[x];
x -= lowbit(x);
}
return cnt;
}
//整体二分
void solve(int l, int r, int L, int R){
//这两个都是结束条件
if (l > r) return;
if (L == R){//更新答案
for (int i = l; i <= r; i++)
if (q[i].type == 3) Ans[q[i].s] = L;
return;
}
int mid = (L + R) >> 1;
for (int i = l; i <= r; i++){
if (q[i].type == 1 && q[i].y <= mid) add(q[i].x, 1);
else if (q[i].type == 2 && q[i].y <= mid) add(q[i].x, -1);
else if (q[i].type == 3) tmp[i] = sum(q[i].y) - sum(q[i].x - 1);
}
//更新完了就要清除标记了
for (int i = l; i <= r; i++){
if (q[i].type == 1 && q[i].y <= mid) add(q[i].x, -1);
else if (q[i].type == 2 && q[i].y <= mid) add(q[i].x, 1);
}
int l1 = 0, l2 = 0;
for (int i = l; i <= r; i++){
if (q[i].type == 3){
//不用id就直接改
if (q[i].cur + tmp[i] > q[i].k - 1) 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 = 1; i <= l1; i++) q[i + l - 1] = q1[i];
for (int i = 1; i <= l2; i++) q[i + l1 + l - 1] = q2[i];
solve(l, l + l1 - 1, L, mid);
solve(l + l1, r, mid + 1, R);
}
void init(){
memset(c, 0, sizeof(c));
cnt = num = 0;//指针初始化,num记录总的操作数量
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++){
num++;
scanf("%d", &data[i]);
q[num].x = i;q[num].type = 1;//1代表插入
q[num].s = 0;q[num].y = data[i];//没有用y就当val用
}
for (int i = 1; i <= m; i++){
char str[2];
num++;
scanf("%s", str);
if (str[0] == 'Q'){
int l, r, k;
scanf("%d%d%d", &l, &r, &k);
q[num].x = l;q[num].y = r;
q[num].type = 3; q[num].s = ++cnt;
q[num].k = k;
}else{
int l, x;
scanf("%d%d", &l, &x);
q[num].x = l;q[num].y = data[l];//2为删除
q[num].type = 2;q[num].s = 0;
q[++num].x = l;
q[num].y = x;//删除后插入
q[num].type = 1;
q[num].s = 0;
data[l] = x;//注意这里一定要改,不然会影响到后面的更新
}
}
for (int i = 1; i <= num; i++) q[i].cur = 0;
}

int main(){
int T;

scanf("%d", &T);
while (T--){
init();
solve(1, num, 0, INF);
for (int i = 1; i <= cnt; i++) printf("%d\n", Ans[i]);
}
return 0;
}


View Code
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: