传送门biu~
先用链表将原序列链在一起,每次一个点的值被减至0时将这个点从链表中删除。即用链表维护每一个点左右第一个值不为0的点。
对于序列上的每一个点开一个值域为[1,n]的权值线段树。对于每个熊孩子区间[l,r],将l作为权值加入线段树r中。当一个点x的值被减至0并将被从链表中删除时,假设点x左边第一个值不为0的点为p,那么线段树x上每一个>p的权值,即每一个熊孩子区间,都会在点x的值减至0后变得高兴。于是我们可以将点x上所有大于p的权值都从线段树上删去并用其更新答案,然后将线段树x合并到线段树p上。
#include<bits/stdc++.h>
#define N 100005
using namespace std;
struct Node{
Node *ch[2];
int sum;
Node(){
ch[0]=ch[1]=0x0;
sum=0;
}
inline void maintain(){
sum=0;
if(ch[0]) sum+=ch[0]->sum;
if(ch[1]) sum+=ch[1]->sum;
}
}*root[N]={new Node};
int n,m,q,ans,a[N],pre[N],nex[N];
void add(Node *&o,int l,int r,int x){
if(!o) o=new Node;
if(l==r){
++o->sum;
return;
}
int mid=l+r>>1;
if(x<=mid) add(o->ch[0],l,mid,x);
else add(o->ch[1],mid+1,r,x);
o->maintain();
}
void del(Node *&o,int l,int r,int x){
if(!o || !o->sum) return;
if(l>=x){
ans+=o->sum;
o=0x0;
return;
}
int mid=l+r>>1;
if(x<=mid) del(o->ch[0],l,mid,x);
del(o->ch[1],mid+1,r,x);
o->maintain();
}
void Merge(Node *&a,Node *b,int l,int r){
if(!b) return;
if(!a) {a=b;return;}
int mid=l+r>>1;
a->sum+=b->sum;
Merge(a->ch[0],b->ch[0],l,mid);
Merge(a->ch[1],b->ch[1],mid+1,r);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
pre[i]=i-1,nex[i]=i+1;
root[i]=new Node;
}
for(int i=1;i<=m;++i){
int l,r;
scanf("%d%d",&l,&r);
add(root[r],1,n,l);
}
scanf("%d",&q);
while(q--){
int x;
scanf("%d",&x);
x=(x+ans-1)%n+1;
if(!--a[x]){
int p=pre[x];
pre[nex[x]]=pre[x];
nex[pre[x]]=nex[x];
del(root[x],1,n,p+1);
Merge(root[p],root[x],1,n);
}
printf("%d\n",ans);
}
return 0;
}