【bzoj 4631】踩气球(链表+线段树合并)

传送门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;
} 

猜你喜欢

转载自blog.csdn.net/zp1ng/article/details/80079986