12
7
2015
0

3110: [Zjoi2013]K大数查询

3110: [Zjoi2013]K大数查询

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


 

 

 

整体二分

 

定义solve(l, r, S)表答案落在[l, r]内或直接影响[l, r]答案的操作集合为S

显然,当 l = r 时,该集合内操作的答案即为r

对于当前答案范围[l, r],我们将其分为[l, mid], [mid + 1, r]两范围

由于我们求的是区间K大数,所以直接影响[mid +1, r]的操作间接影响[l, mid]

所以我们将直接影响[mid + 1, r]的操作施行在数据结构中(线段树 或 树状数组)

    维护区间内[mid + 1, r]中数的个数

对于每个询问,在数据结构中求出给定区间内[mid + 1, r]中数的个数res

若 res >= k, 则该区间第k大数在[mid + 1, r]中

若 res < k, 则区间第k大数在[l, mid]中

将操作集合S中的数按答案或直接影响范围排序,然后用操作序列的首尾位置表示集合

递归solve(l, mid, S1), solve(mid + 1, r, S2)

 

#include <cstdio>
#include <algorithm>
using namespace std;
const int MaxN = 50010;
const int MaxS = 250010;
int N, M;
struct opt{
	int o, l, r, c, key, k;
}p[MaxN], que1[MaxN], que2[MaxN];
int clear[MaxS], ma[MaxS], sum[MaxS];
void Down(int k, int l, int r){
	if (l == r) return;
	int mid = l + r >> 1;
	if (clear[k]) {
		clear[k << 1] = clear[(k << 1) + 1] = 1;
		ma[k << 1] = ma[(k << 1) + 1] = 0;
		sum[k << 1] = sum[(k << 1) + 1] = 0;
	    clear[k] = 0;
	}
	if (ma[k]) {
		ma[k << 1] += ma[k], ma[(k << 1) + 1] += ma[k];
		sum[k << 1] += ma[k] * (mid - l + 1);
		sum[(k << 1) + 1] += ma[k] * (r - mid);
		ma[k] = 0;
	}
} 
void Clear(){
	clear[1] = 1;
	ma[1] = sum[1] = 0;
}
void Insert(int k, int l, int r, int L, int R, int s){
	if (l > R || L > r) return;
	if (L <= l && r <= R) {
		ma[k] += s;
		sum[k] += s * (r - l + 1);
		return;
	}
	int mid = l + r >> 1;
	Down(k, l, r);
	Insert(k << 1, l, mid, L, R, s), Insert((k << 1) + 1, mid + 1, r, L, R, s);
	sum[k] = sum[k << 1] + sum[(k << 1) + 1];
}
int Query(int k, int l, int r, int L, int R){
	if (l > R || L > r) return 0;
	if (L <= l && r <= R) return sum[k];
	int mid = l + r >> 1;
	int res = 0;
	Down(k, l, r);
	res += Query(k << 1, l, mid, L, R);
	res += Query((k << 1) + 1, mid + 1, r, L ,R);
	sum[k] = sum[k << 1] + sum[(k << 1) + 1];
	return res;
}
void solve(int l, int r, int pl, int pr){
	if (pl > pr) return;
	if (l == r) {
		for (int i = pl; i <= pr; ++i)
		    p[i].key = l;
		return;
	} 
	int mid = l + r >> 1;
	int t1 = 0, t2 = 0;
	for (int i = pl; i <= pr; ++i)
	    if (p[i].o == 1) {
	    	if (mid + 1 <= p[i].c) {
	    	    Insert(1, 1, N, p[i].l, p[i].r, 1);
	    	    que2[++t2] = p[i];
			}
			else que1[++t1] = p[i];
		}
		else {
			int res = Query(1, 1, N, p[i].l, p[i].r);
			if (res >= p[i].c) que2[++t2] = p[i];
			else que1[++t1] = p[i], que1[t1].c -= res;
		}
	for (int i = 1; i <= t1; ++i) p[pl + i - 1] = que1[i];
	for (int i = 1; i <= t2; ++i) p[pl + t1 +i - 1] = que2[i];
	Clear();
	solve(l, mid, pl, pl + t1 - 1);
	solve(mid + 1, r, pl + t1, pr); 
}
bool cmp(const opt &a, const opt &b){
	return a.k < b.k;
}
int main(){
	scanf("%d%d", &N, &M);
	for (int i = 1; i <= M; ++i)
	    scanf("%d%d%d%d", &p[i].o, &p[i].l, &p[i].r, &p[i].c), p[i].k = i;
	solve(-N, N, 1, M);
	sort(p + 1, p + M + 1, cmp);
	for (int i = 1; i <= M; ++i)
	    if (p[i].o == 2)
	        printf("%d\n", p[i].key);
} 
Category: BZOJ | Tags: 分治 bzoj 线段树 | Read Count: 389

登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter

Host by is-Programmer.com | Power by Chito 1.3.3 beta | Theme: Aeros 2.0 by TheBuckmaker.com