zoukankan      html  css  js  c++  java
  • [BZOJ3110][Zjoi2013]K大数查询

    [BZOJ3110][Zjoi2013]K大数查询

    试题描述

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

    输入

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

    输出

    输出每个询问的结果

    输入示例

    2 5
    1 1 2 1
    1 1 2 2
    2 1 1 2
    2 1 1 1
    2 1 2 3

    输出示例

    1
    2
    1

    数据规模及约定

    N,M<=50000,N,M<=50000
    a<=b<=N
    1操作中abs(c)<=N
    2操作中c<=Maxlongint

    题解

    线段树套线段树。第一维记录权值,第二维记录位置;具体意思指,对于第一维线段树上的一个节点 [L, R],它所指向的那颗线段树中记录的是每个位置包含了几个值在 [L, R] 范围内的数。

    这题不能用线段树套平衡树(外层记录位置,内层维护权值)的原因:对于套在外面那一层的线段树是很难进行区间操作的,所以这题就需要换一个思路,把位置信息放在内层的线段树中。

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cctype>
    #include <algorithm>
    using namespace std;
    
    #define maxn 50010
    #define maxnode 20000005
    #define LL long long
    int n;
    
    int ToT, lc[maxnode], rc[maxnode];
    LL addv[maxnode], sumv[maxnode];
    int ql, qr, v;
    void add(int& o, int L, int R) {
    	if(!o) o = ++ToT;
    //  printf("o: %d [%d, %d] [%d, %d]
    ", o, L, R, ql, qr);
    	if(ql <= L && R <= qr){ sumv[o] += R - L + 1; addv[o]++; return ; }
    	int M = L + R >> 1;
    	if(ql <= M) add(lc[o], L, M);
    	if(qr > M) add(rc[o], M+1, R);
    	sumv[o] = sumv[lc[o]] + sumv[rc[o]] + addv[o] * (R - L + 1);
    	return ;
    }
    LL query(int o, int L, int R, LL Add) {
    	if(!o) {
    		int l = max(L, ql), r = min(R, qr);
    		return Add * (r - l + 1);
    	}
    	if(ql <= L && R <= qr) return sumv[o] + Add * (R - L + 1);
    	int M = L + R >> 1; LL ans = 0;
    	if(ql <= M) ans += query(lc[o], L, M, Add + addv[o]);
    	if(qr > M) ans += query(rc[o], M+1, R, Add + addv[o]);
    	return ans;
    }
    
    int rt[maxn<<3];
    void update() {
    	int o = 1, L = 1, R = n << 1 | 1;
    	while(L < R) {
    		add(rt[o], 1, n);
    		int M = L + R >> 1, ls = o << 1, rs = ls | 1;
    		if(v <= M) R = M, o = ls;
    		else L = M + 1, o = rs;
    	}
    	return add(rt[o], 1, n);
    }
    int query() {
    	int o = 1, L = 1, R = n << 1 | 1;
    	while(L < R) {
    		int M = L + R >> 1, ls = o << 1, rs = ls | 1;
    		LL rsz = query(rt[rs], 1, n, 0);
    //		printf("%d %d %lld %lld
    ", L, R, rsz, v);
    		if(v <= rsz) L = M + 1, o = rs;
    		else R = M, o = ls, v -= rsz;
    	}
    	return L;
    }
    
    int main() {
    	int q, tp;
    	scanf("%d%d", &n, &q);
    	while(q--) {
    		scanf("%d%d%d%d", &tp, &ql, &qr, &v);
    		if(tp == 1) v += n + 1, update();
    		if(tp == 2) printf("%d
    ", query() - n - 1);
    	}
    	
    	return 0;
    }
    

    这是卡常神题啊!!!我调了半天最后把内层线段树改成静态懒标记(就是标记不下传,据说这个叫标记永久化?)才通过的。。。

  • 相关阅读:
    bzoj1009
    bzoj1576 3694
    bzoj3143
    bzoj1391
    bzoj2729
    bzoj2653
    bzoj3261
    bzoj2326
    人件
    优秀的产品
  • 原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/6297880.html
Copyright © 2011-2022 走看看