【bzoj4631】踩气球 线段树

题目描述

给出一个长度为 $n$ 的序列,序列中每一个数都是正整数。现在给出 $m$ 个指定区间以及 $q$ 次操作,每次操作将某个位置的数-1(最多减到0),并询问有多少个指定区间的区间和为0。强制在线。

输入

第一行包含两个正整数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


题解

线段树

考虑朴素暴力:每次操作一个位置,如果该数减到了0,则将所有包含该位置的区间的区间长度-1。当某个区间的区间长度减到0时则答案+1。

那么如何优化这个暴力呢?线段树。

一个区间在线段树上对应着至多log个点,记录这个点数。我们对每一个点开一个vector,如果一个区间对应着这个点则加入到这个vector中。

维护区间和,对于修改操作将路径上的点的区间和-1。如果一个点的区间和减到0,则将其vector中对应的区间的点数-1,区间对应点数减到0则答案+1。

时间复杂度 $O(n\log n)$

#include <cstdio>
#include <vector>
#include <algorithm>
#define N 100010
#define lson l , mid , x << 1
#define rson mid + 1 , r , x << 1 | 1
using namespace std;
typedef long long ll;
vector<int> v[N << 2];
ll sum[N << 2];
int c[N] , ans;
inline void pushup(int x)
{
	sum[x] = sum[x << 1] + sum[x << 1 | 1];
}
void build(int l , int r , int x)
{
	if(l == r)
	{
		scanf("%lld" , &sum[x]);
		return;
	}
	int mid = (l + r) >> 1;
	build(lson) , build(rson);
	pushup(x);
}
void init(int b , int e , int id , int l , int r , int x)
{
	if(b <= l && r <= e)
	{
		v[x].push_back(id) , c[id] ++ ;
		return;
	}
	int mid = (l + r) >> 1;
	if(b <= mid) init(b , e , id , lson);
	if(e > mid) init(b , e , id , rson);
}
void solve(int p , int l , int r , int x)
{
	sum[x] -- ;
	if(!sum[x])
	{
		vector<int>::iterator i;
		for(i = v[x].begin() ; i != v[x].end() ; i ++ )
		{
			c[*i] -- ;
			if(!c[*i]) ans ++ ;
		}
	}
	if(l == r) return;
	int mid = (l + r) >> 1;
	if(p <= mid) solve(p , lson);
	else solve(p , rson);
}
int main()
{
	int n , m , q , i , x , y;
	scanf("%d%d" , &n , &m);
	build(1 , n , 1);
	for(i = 1 ; i <= m ; i ++ ) scanf("%d%d" , &x , &y) , init(x , y , i , 1 , n , 1);
	scanf("%d" , &q);
	while(q -- )
	{
		scanf("%d" , &x);
		solve((x + ans - 1) % n + 1 , 1 , n , 1);
		printf("%d\n" , ans);
	}
	return 0;
}

 

 

posted @ 2017-12-12 09:57  GXZlegend  阅读(363)  评论(0编辑  收藏  举报