[bzoj4631]踩气球

来源:互联网 发布:文学少女知乎 编辑:程序博客网 时间:2024/04/28 22:51

题目大意

有一个序列,每次将一个位置的数减去1(保证这个数大于0)
有m个区间,每次操作后你都要输出有多少个区间和为0。
强制在线。

线段树搞搞

这m个区间可以被分割到线段树上的log个区间,那我们就把这些区间挂上去。
线段树维护区间和(每个位置可以只用0或1表示)
每次修改时如果一个区间和为0,就处理所有挂在上面的区间。
具体的,可以用d[i]表示第i个区间被分成了多少段,那么就是把挂在上面的区间d值均减一,d值为0显然就是区间和为0了。

#include<cstdio>#include<algorithm>#define fo(i,a,b) for(i=a;i<=b;i++)using namespace std;const int maxn=100000+10;int sum[maxn*4],h[maxn*4],go[maxn*25],next[maxn*25];int d[maxn],a[maxn];int i,j,k,l,t,n,m,q,tot,ans;void add(int x,int y){    d[y]++;    go[++tot]=y;    next[tot]=h[x];    h[x]=tot;}void build(int p,int l,int r){    if (l==r){        sum[p]=1;        return;    }    int mid=(l+r)/2;    build(p*2,l,mid);    build(p*2+1,mid+1,r);    sum[p]=sum[p*2]+sum[p*2+1];}void cover(int p,int l,int r,int a,int b){    if (l==a&&r==b){        add(p,i);        return;    }    int mid=(l+r)/2;    if (b<=mid) cover(p*2,l,mid,a,b);    else if (a>mid) cover(p*2+1,mid+1,r,a,b);    else{        cover(p*2,l,mid,a,mid);        cover(p*2+1,mid+1,r,mid+1,b);    }}void change(int p,int l,int r,int a){    sum[p]--;    if (!sum[p]){        int t=h[p];        while (t){            d[go[t]]--;            if (!d[go[t]]) ans++;            t=next[t];        }    }    if (l==r) return;    int mid=(l+r)/2;    if (a<=mid) change(p*2,l,mid,a);else change(p*2+1,mid+1,r,a);}int main(){    freopen("bal.in","r",stdin);    scanf("%d%d",&n,&m);    build(1,1,n);    fo(i,1,n) scanf("%d",&a[i]);    fo(i,1,m){        scanf("%d%d",&j,&k);        cover(1,1,n,j,k);    }    scanf("%d",&q);    while (q--){        scanf("%d",&j);        j=(j+ans-1)%n+1;        a[j]--;        if (!a[j]) change(1,1,n,j);        printf("%d\n",ans);    }}
0 0
原创粉丝点击