【BZOJ4631】踩气球

题目大意 : 有一个 \(n\) 个数的正整数序列 \(a\) ,给定 \(m\) 个区间 $[l_i,r_i] $ 和 \(q\) 次操作,每次操作将序列中某个数 \(a_i\) 减1.现询问每次操作后有多少个区间满足区间内所有的数都为 \(0\) 。数据强制在线,所有给定数不超过 \(10^5\)

Tag: 线段树、链表

将每个区间拆分成线段树上的若干个区间,用链表维护每个线段树上的区间被哪些区间所包含,同时用 \(num[i]\) 记录第 \(i\) 区间拆分出的区间数量,这样做修改操作时若一个区间和为 \(0\) ,可将包含其的区间的 \(num\)\(1\) 。若此时 \(num\)\(0\) ,则答案 \(+1\) 。这样能做到 \(O(q{\rm log}n)\) 处理询问。

#include<cstdio>
inline int _read()
{
	char c; int x=0;
	for(;c<'0'||c>'9';c=getchar());
	for(;c>='0'&&c<='9';c=getchar())x=(x<<1)+(x<<3)+c-'0';
	return x;
}
const int N=100005;
int st[N*4],n,m,ans,now;
#define mid (l+r>>1)
#define lx (x<<1)
#define rx (x<<1|1)
int head[N*40],nxt[N*40],to[N*40],tot,num[N];
void addedge(int u, int v)
{
	nxt[++tot]=head[u]; head[u]=tot;
	to[tot]=v;
}
void build(int x, int l, int r)
{
	if(l==r)
	{
		st[x]=_read();
		return ;
	}
	build(lx,l,mid); build(rx,mid+1,r);
	st[x]=st[lx]+st[rx];
}
void addrange(int x, int l, int r, int sl, int sr)
{
	if(!st[x]) return ;
	if(sl<=l&&r<=sr)
	{
		addedge(x,now); //线段树区间x包含第now个区间,加入链表
		++num[now];
		return ;
	}
	if(sl<=mid) addrange(lx,l,mid,sl,sr);
	if(sr>mid) addrange(rx,mid+1,r,sl,sr);
}
void update(int x, int l, int r, int s)
{
	st[x]--;
	if(!st[x])
		for(int e=head[x];e;e=nxt[e])
		{
			num[to[e]]--;
			if(!num[to[e]]) ++ans;
		}
	if(l==r) return ;
	if(s<=mid) update(lx,l,mid,s);
	else update(rx,mid+1,r,s);
}
int main()
{
	n=_read(),m=_read();
	build(1,1,n);
	for(int i=1;i<=m;i++)
	{
		int l=_read(),r=_read();
		now=i;
		addrange(1,1,n,l,r);
	}
	int q=_read();
	while(q--)
	{
		int x=(long long)(_read()+ans-1)%n+1;
		update(1,1,n,x);
		printf("%d\n",ans);
	}
}
posted @ 2018-07-21 16:55  x_faraway_x  阅读(117)  评论(0)    收藏  举报