您的位置:首页 > 其它

【BZOJ3110】【整体二分+树状数组区间修改/线段树】K大数查询

2015-03-15 17:09 519 查看

Description

有N个位置,M个操作。操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c

如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少。

Input

第一行N,M

接下来M行,每行形如1 a b c或2 a b c

Output

输出每个询问的结果

Sample Input

2 5

1 1 2 1

1 1 2 2

2 1 1 2

2 1 1 1

2 1 2 3

Sample Output

1

2

1

HINT

【样例说明】

第一个操作 后位置 1 的数只有 1 , 位置 2 的数也只有 1 。 第二个操作 后位置 1

的数有 1 、 2 ,位置 2 的数也有 1 、 2 。 第三次询问 位置 1 到位置 1 第 2 大的数 是

1 。 第四次询问 位置 1 到位置 1 第 1 大的数是 2 。 第五次询问 位置 1 到位置 2 第 3

大的数是 1 。‍

N,M<=50000,N,M<=50000

a<=b<=N

1操作中abs(c)<=N

2操作中abs(c)<=Maxlongint

Source

【分析】

我可以吐槽一下数据很水吗?1A了

表示整体二分大法好,离线第K大都不怕。

区间修改用线段树和树状数组都可以水。

/*
明代杨慎
《临江仙·滚滚长江东逝水》

滚滚长江东逝水,浪花淘尽英雄。
是非成败转头空。
青山依旧在,几度夕阳红。
白发渔樵江渚上,惯看秋月春风。
一壶浊酒喜相逢。
古今多少事,都付笑谈中。
*/
#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 = 0x7fffffff;
const int MAXN = 300000 + 10;
using namespace std;
//整体二分+树状数组的区间修改!
struct QUESTION{
int l, r;
int k, s, cur, type;
}q[MAXN];
int id[MAXN];
int Max, Min, n, m, Ans[MAXN];
int c[2][MAXN];//0是普通和,1是全部和
int tmp[MAXN], q1[MAXN], q2[MAXN], cnt;

int lowbit(int x) {return x & -x;}
int sum(int k, int x){
int cnt = 0;
while (x > 0){
cnt += c[k][x];
x -= lowbit(x);
}
return cnt;
}
void add(int k, int x, int val){
while (x <= n){
c[k][x] += val;
x += lowbit(x);
}
return;
}
//得到一个点真实的前缀和
int get(int x){
if (x == 2)
printf("");
return sum(1, x) - sum(0, x) * (n - x);
}
//整体二分
void solve(int l, int r, int L, int R){
if (l > r || L == R) return;

int  mid = (L + R) >> 1;
for (int i = l; i <= r; i++){
//区间加
if (q[id[i]].type == 1 && q[id[i]].k >= mid){//注意是区间第k大
int a = q[id[i]].l, b = q[id[i]].r;
add(0, a, 1);
add(0, b + 1, -1);
add(1, a, n - a + 1);
add(1, b + 1, - (n - (b + 1) + 1));
}else if (q[id[i]].type == 2) tmp[id[i]] = get(q[id[i]].r) - get(q[id[i]].l - 1);
}
//清楚标记
for (int i = l; i <= r; i++){
if (q[id[i]].type == 1 && q[id[i]].k >= mid){
int a = q[id[i]].l, b = q[id[i]].r;
add(0, a, -1);
add(0, b + 1, 1);
add(1, a, -(n - a + 1));
add(1, b + 1, (n - (b + 1) + 1));
}
}
int l1 = 0, l2 = 0;
//q1放右边,q2放左边
for (int i = l; i <= r; i++){
if (q[id[i]].type == 2){
//这个要放在右边
if (q[id[i]].cur + tmp[id[i]] > q[id[i]].k - 1){
q1[++l1] = id[i];
Ans[q[id[i]].s] = mid;
}else{
q[id[i]].cur += tmp[id[i]];
q2[++l2] = id[i];
}
}else{
if (q[id[i]].k >= mid) q1[++l1] = id[i];
else q2[++l2] = id[i];
}
}

for (int i = 1; i <= l2; i++) id[i + l - 1] = q2[i];
for (int i = 1; i <= l1; i++) id[i + l2 + l - 1] = q1[i];
solve(l, l + l2 - 1, L, mid);
solve(l + l2, r, mid + 1, R);
}
void init(){
memset(c, 0, sizeof(c));
scanf("%d%d", &n, &m);
cnt = 0;//cnt用来记录询问问题个数
Min = INF, Max = -INF;
for (int i = 1; i <= m; i++){
int t;
scanf("%d", &t);
if (t == 1){//插入操作
int l, r, x;
scanf("%d%d%d", &l, &r, &x);
q[i].type =  1;
q[i].l = l; q[i].r = r;
q[i].k = x; q[i].s = 0;
q[i].cur = 0;
Max = max(Max, x);
Min = min(Min, x);
}else if (t == 2){//询问操作
int l, r, x;
scanf("%d%d%d", &l, &r, &x);
q[i].type = 2;
q[i].l = l; q[i].r = r;
q[i].k = x; q[i].cur = 0;
q[i].s = ++cnt;//注意这里x是第k大
}
}
for (int i = 1; i <= m; i++) id[i] = i;
//printf("%d %d\n", Max, Min);
}

int main(){
int T;

init();
solve(1, m, Min, Max + 1);
for (int i = 1; i <= cnt; i++) printf("%d\n", Ans[i]);
return 0;
}


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