1496 -- 踩气球(Solution)
题目大意 : 有一个 \(n\) 个数的正整数序列 \(a\) ,给定 \(m\) 个区间 $[l_i,r_i] $ 和 \(q\) 次操作,每次操作将序列中某个数 \(a_i\) 减1.现询问每次操作后有多少个区间满足区间内所有的数都为 \(0\) 。数据强制在线,所有给定数不超过 \(10^5\) 。
Tag: 线段树、链表
Analysis By LC:
将每个区间拆分成线段树上的若干个区间,用链表维护每个线段树上的区间被哪些区间所包含,同时用 \(num[i]\) 记录第 \(i\) 区间拆分出的区间数量,这样做修改操作时若一个区间和为 \(0\) ,可将包含其的区间的 \(num\) 减 \(1\) 。若此时 \(num\) 为 \(0\) ,则答案 \(+1\) 。这样能做到 \(O(q{\rm log}n)\) 处理询问。
Code By LC :
#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);
}
}