• [BZOJ4631]踩气球


    [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;
    }
    
  • 相关阅读:
    手机网络制式常识
    合并两个有序数组a和b到c
    N皇后问题
    数独求解
    ARM处理器模式
    ARM异常中断处理
    国风·召南·野有死麕
    八大排序算法
    1045 Favorite Color Stripe (最长不下降子序列 LIS 或最长公共子序列 LCS)
    1066 Root of AVL Tree (模拟AVL建树)
  • 原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/5688768.html
Copyright © 2020-2023  润新知