[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\n", 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\n", mn[u], mx[u]);
			del(root);
		}
		printf("%d\n", ans);
	}
	
	return 0;
}

 

版权声明:本文为xiao-ju-ruo-xjr原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/5688768.html