[BZOJ4631]踩气球
试题描述
六一儿童节到了, SHUXK 被迫陪着M个熊孩子玩一个无聊的游戏:有N个盒子从左到右排成一排,第i个盒子里装着Ai个气球。
SHUXK 要进行Q次操作,每次从某一个盒子里拿出一个没被踩爆的气球,然后熊孩子们就会立刻把它踩爆。
这M个熊孩子每个人都指定了一个盒子区间[Li, Ri]。 如果某一个时刻,一个熊孩子发现自己选定的盒子区间[Li, Ri]中的所
有气球都已经被踩爆了,他就会非常高兴(显然之后他一直会很高兴)。
为了不辜负将自己的任务强行塞给 SHUXK 的那个人的期望, SHUXK 想向你询问:
他每次操作过后会有多少个熊孩子很高兴。
输入
第一行包含两个正整数N和M,分别表示盒子和熊孩子的个数。
第二行包含N个正整数Ai( 1 < = Ai < = 10^5),表示每个盒子里气球的数量。
以下M行每行包含两个正整数Li, Ri( 1 < = Li < = Ri < = N),分别表示每一个熊孩子指定的区间。
以下一行包含一个正整数Q,表示 SHUXK 操作的次数。
以下Q行每行包含一个正整数X,表示这次操作是从第X个盒子里拿气球。为
了体现在线,我们对输入的X进行了加密。
假设输入的正整数是x',那么真正的X = (x' + Lastans − 1)Mod N + 1。其
中Lastans为上一次询问的答案。对于第一个询问, Lastans = 0。
输入数据保证1 < = x' < = 10^9, 且第X个盒子中有尚未被踩爆的气球。
N < = 10^5 ,M < = 10^5 �,Q < = 10^5
输出
包含Q行,每行输出一个整数,表示 SHUXK 一次操作后询问的
答案。答案的顺序应与输入数据的顺序保持一致。
输入示例
5 3 1 1 1 1 1 5 5 2 2 1 3 5 4 2 5 2 3
输出示例
0 1 1 2 3
数据规模及约定
见“输入”
题解
解法1
考虑预处理所有熊孩子。把第 i 个熊孩子想象成一个坐标为 (Li, Ri) 的点,点权为 A 数组 Li 到 Ri 的连续和。那么对于一个操作 x(把 x 号箱子中的一个气球踩爆),就是平面上 (x, x) 这个点的左上区域所有点权值减 1. 于是可以用 kd 树维护,支持区间减 1 操作,每次减 1 操作结束后看最小值是否减到 0,若减到 0,暴力将所有权值减到零的点删除,所以 kd 树还需要维护最小权值和最小权值所在位置。注意这里每个点最多被删除一次,每次删除复杂度为 O(logn),所以总时间复杂度为 O(n·sqrt(n) + n·logn)。
我写了一发,然而 T 飞了。。。
解法2
我们对原序列 A 进行操作,每次将某个位置的值 Ai 减 1. 考虑使用并查集维护当前最大的区间 [L, R] 使其满足区间内所有数都为 0,对于一个操作 x,若位置 x 的值被减到了 0,则和它左右两个相邻的减到 0 的位置合并,得到这个最大的区间 [L, R]。这次我们仍然把所有的熊孩子看成一个点加入到 kd 树中,但点权初始都为 1. 这样对于一个所有数都为 0 的区间 [L, R],在平面内找到 (L, R) 这个点,则需要统计的就是 [L, R] 包含的所有熊孩子区间,在平面上就是 (L, R) 右下方的点的个数,注意判重,处理方法:统计完区域的点数(即所有点权值和)后,将该区域所有点权赋 0. kd 树 O(n·sqrt(n)),并查集 O(n·logn)(我只写了路径压缩)。
这个算法跑得飞快。。。
结论:kd 树在涉及到区域操作时,复杂度为 O(玄学)。
#include <iostream> #include <cstdio> #include <algorithm> #include <cmath> #include <stack> #include <vector> #include <queue> #include <cstring> #include <string> #include <map> #include <set> using namespace std; const int BufferSize = 1 << 16; char buffer[BufferSize], *Head, *Tail; inline char Getchar() { if(Head == Tail) { int l = fread(buffer, 1, BufferSize, stdin); Tail = (Head = buffer) + l; } return *Head++; } int read() { int x = 0, f = 1; char c = Getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); } return x * f; } #define maxn 100010 #define oo 2147483647 #define LL long long int n, m, lc[maxn], rc[maxn], Cur, A[maxn], root, ans; int fa[maxn], mn[maxn], mx[maxn]; int findset(int x){ return x == fa[x] ? x : fa[x] = findset(fa[x]); } struct Node { int x[2], mx[2], mn[2], setv, sumv, has; bool operator < (const Node& t) const { return x[Cur] < t.x[Cur]; } } ns[maxn], x; void maintain(int o) { int l = lc[o], r = rc[o]; for(int i = 0; i < 2; i++) { ns[o].mx[i] = max(max(ns[l].mx[i], ns[r].mx[i]), ns[o].x[i]); ns[o].mn[i] = min(min(ns[l].mn[i], ns[r].mn[i]), ns[o].x[i]); } ns[o].sumv = ns[l].sumv + ns[r].sumv + ns[o].has; return ; } void build(int& o, int L, int R, int cur) { if(L > R){ o = 0; return ; } int M = L + R >> 1; o = M; Cur = cur; nth_element(ns + L, ns + M, ns + R + 1); build(lc[o], L, M - 1, cur ^ 1); build(rc[o], M + 1, R, cur ^ 1); maintain(o); return ; } void pushdown(int o) { int l = lc[o], r = rc[o]; if(ns[o].setv >= 0) { ns[o].sumv = ns[o].has = 0; ns[l].setv = ns[r].setv = 0; ns[o].setv = -1; } return ; } bool all(int o) { return ns[o].mn[0] >= x.x[0] && ns[o].mx[1] <= x.x[1]; } bool has(int o) { return ns[o].mx[0] >= x.x[0] && ns[o].mn[1] <= x.x[1]; } void del(int o) { if(!o) return ; pushdown(o); int l = lc[o], r = rc[o]; if(all(l)){ ns[l].setv = 0; ans += ns[l].sumv; ns[l].sumv = ns[l].has = 0; } else if(has(l)) del(l); if(all(r)){ ns[r].setv = 0; ans += ns[r].sumv; ns[r].sumv = ns[r].has = 0; } else if(has(r)) del(r); if(ns[o].x[0] >= x.x[0] && ns[o].x[1] <= x.x[1]){ ans += ns[o].has; ns[o].has = 0; } maintain(o); return ; } int main() { // freopen("data.in", "r", stdin); // freopen("data.out", "w", stdout); ns[0].mx[0] = ns[0].mx[1] = -oo; ns[0].mn[0] = ns[0].mn[1] = oo; n = read(); m = read(); for(int i = 1; i <= n; i++) A[i] = read(); for(int i = 1; i <= m; i++) { int L = read(), R = read(); ns[i].x[0] = L; ns[i].x[1] = R; ns[i].setv = -1; ns[i].has = 1; } build(root, 1, m, 0); int q = read(); for(int i = 1; i <= n; i++) fa[i] = mx[i] = mn[i] = i; while(q--) { // int p = read(); int p = (read() + ans - 1) % n + 1; // printf("p: %d ", p); if(!(--A[p])) { int u, v; if(p && !A[p-1]) { u = findset(p-1); v = findset(p); if(u != v) fa[v] = u, mn[u] = min(mn[u], mn[v]), mx[u] = max(mx[u], mx[v]); } if(p < n && !A[p+1]) { u = findset(p+1); v = findset(p); if(u != v) fa[v] = u, mn[u] = min(mn[u], mn[v]), mx[u] = max(mx[u], mx[v]); } u = findset(p); x.x[0] = mn[u]; x.x[1] = mx[u]; // printf("%d %d ", mn[u], mx[u]); del(root); } printf("%d ", ans); } return 0; }