[ZJOI2013]K大数查询 题解
题目地址:洛谷:【P3332】[ZJOI2013]K大数查询 – 洛谷、BZOJ:Problem 3110. — [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
输出格式:
输出每个询问的结果
输入输出样例
输入样例#1:
2 5 1 1 2 1 1 1 2 2 2 1 1 2 2 1 1 1 2 1 2 3
输出样例#1:
1 2 1
说明
【样例说明】
第一个操作 后位置 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操作中c<=long long
题解
可以用线段树套线段树的写法。我们在外面维护一个权值线段树,每个节点挂一个普通的区间求和线段树,这样,我们就可以在利用权值线段树查询k大的时候,通过普通线段树查到询问区间内的数字个数。总复杂度O(n \log^2 n)可以接受,但是这种方法常数较大,洛谷不开O2无法通过,且需要动态开点,否则容易爆内存。
代码
// Code by KSkun, 2018/5
#include <cstdio>
#include <cctype>
#include <algorithm>
typedef long long LL;
inline char fgc() {
static char buf[100000], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2)
? EOF : *p1++;
}
inline LL readint() {
register LL res = 0, neg = 1;
register char c = fgc();
while(!isdigit(c)) {
if(c == '-') neg = -1;
c = fgc();
}
while(isdigit(c)) {
res = (res << 1) + (res << 3) + c - '0';
c = fgc();
}
return res * neg;
}
const int MAXN = 100005;
struct SGTNode {
int lch, rch;
LL tag, val;
} sgt[MAXN * 200];
int stot;
inline void pushdown(int o, int l, int r) {
int mid = (l + r) >> 1;
if(sgt[o].tag) {
if(!sgt[o].lch) sgt[o].lch = ++stot;
sgt[sgt[o].lch].val += sgt[o].tag * (mid - l + 1);
sgt[sgt[o].lch].tag += sgt[o].tag;
if(!sgt[o].rch) sgt[o].rch = ++stot;
sgt[sgt[o].rch].val += sgt[o].tag * (r - mid);
sgt[sgt[o].rch].tag += sgt[o].tag;
sgt[o].tag = 0;
}
}
struct SegmentTree {
int rt;
inline void add(int &o, int l, int r, int ll, int rr, int v) {
if(!o) o = ++stot;
if(l >= ll && r <= rr) {
sgt[o].tag += v;
sgt[o].val += v * (r - l + 1);
return;
}
pushdown(o, l, r);
int mid = (l + r) >> 1;
if(ll <= mid) add(sgt[o].lch, l, mid, ll, rr, v);
if(rr > mid) add(sgt[o].rch, mid + 1, r, ll, rr, v);
sgt[o].val = sgt[sgt[o].lch].val + sgt[sgt[o].rch].val;
}
inline LL query(int o, int l, int r, int ll, int rr) {
if(l >= ll && r <= rr) {
return sgt[o].val;
}
pushdown(o, l, r);
int mid = (l + r) >> 1; LL res = 0;
if(ll <= mid) res += query(sgt[o].lch, l, mid, ll, rr);
if(rr > mid) res += query(sgt[o].rch, mid + 1, r, ll, rr);
return res;
}
} sts[MAXN << 2];
int n, m;
#define lch o << 1
#define rch o << 1 | 1
#define mid ((l + r) >> 1)
inline void insert(int o, int l, int r, int x, int il, int ir) {
sts[o].add(sts[o].rt, 1, n, il, ir, 1);
if(l == r) return;
if(x <= mid) insert(lch, l, mid, x, il, ir);
else insert(rch, mid + 1, r, x, il, ir);
}
inline int query(int o, int l, int r, LL k, int ql, int qr) {
if(l == r) return l;
LL rsiz = sts[rch].query(sts[rch].rt, 1, n, ql, qr);
if(k <= rsiz) return query(rch, mid + 1, r, k, ql, qr);
else return query(lch, l, mid, k - rsiz, ql, qr);
}
int op, a, b; LL c;
int main() {
n = readint(); m = readint();
while(m--) {
op = readint(); a = readint(); b = readint(); c = readint();
if(op == 1) insert(1, 0, n << 1, c + n, a, b);
if(op == 2) printf("%d\n", query(1, 0, n << 1, c, a, b) - n);
}
return 0;
}