分析:这道题和以前做过的模拟赛题很像:传送门.
对于前30%的数据可以直接暴力求,k=1的数据利用线段树求区间最大值,没有修改操作可以用主席树.100%的数据主席树是肯定用不了的,观察到K非常小,可以用线段树来暴力维护.
线段树记录每个区间内的第k小值(1≤k≤10),其它的操作没啥变化,就是pushup的时候要把两棵子树的信息给合并起来,用两个指针就好了.
要维护的信息特别少,并且可以用线段树来维护的可以考虑用线段树来暴力维护.
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int maxn = 100010; struct node { int minn[20]; }e[maxn << 2]; int n, m, tag[maxn << 2]; node hebing(node a, node b) { int p1 = 1, p2 = 1; node temp; for (int i = 1; i <= 10; i++) { if (a.minn[p1] > b.minn[p2]) temp.minn[i] = a.minn[p1++]; else temp.minn[i] = b.minn[p2++]; } return temp; } void pushup(int o) { e[o] = hebing(e[o * 2], e[o * 2 + 1]); } void color(int o, int v) { tag[o] += v; for (int i = 1; i <= 10; i++) if (e[o].minn[i]) e[o].minn[i] += v; } void pushdown(int o) { if (tag[o]) { color(o * 2, tag[o]); color(o * 2 + 1, tag[o]); tag[o] = 0; } } void build(int o, int l, int r) { if (l == r) { scanf("%d", &e[o].minn[1]); return; } int mid = (l + r) >> 1; build(o * 2, l, mid); build(o * 2 + 1, mid + 1, r); pushup(o); } void update(int o, int l, int r, int x, int y, int v) { if (x <= l && r <= y) { color(o, v); return; } pushdown(o); int mid = (l + r) >> 1; if (x <= mid) update(o * 2, l, mid, x, y, v); if (y > mid) update(o * 2 + 1, mid + 1, r, x, y, v); pushup(o); } node query(int o, int l, int r, int x, int y) { if (x <= l && r <= y) return e[o]; pushdown(o); int mid = (l + r) >> 1; node temp; bool flag = false; if (x <= mid) { if (!flag) temp = query(o * 2, l, mid, x, y); else temp = hebing(temp, query(o * 2, l, mid, x, y)); flag = 1; } if (y > mid) { if (!flag) temp = query(o * 2 + 1, mid + 1, r, x, y); else temp = hebing(temp, query(o * 2 + 1, mid + 1, r, x, y)); } return temp; } int main() { scanf("%d%d", &n, &m); build(1, 1, n); for (int i = 1; i <= m; i++) { int op, l, r, k; scanf("%d%d%d%d", &op, &l, &r, &k); if (op == 0) { if (r - l + 1 < k) printf("-1\n"); else printf("%d\n", query(1, 1, n, l, r).minn[k]); } else update(1, 1, n, l, r, k); } return 0; }