踩气球 - 线段树

题目大意:给你n个数Ai和m个区间,每次让某个Ax–,然后询问有多少个区间的和是0。任意时刻Ai>=0。强制在线。
题解:直接线段树把区间拆开即可。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
#define lint long long
#define gc getchar()
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define Rep(i,v) rep(i,0,(int)v.size()-1)
#define N 100010
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
int ans,a[N],cnt[N];
inline int inn()
{
    int x,ch;while((ch=gc)<'0'||ch>'9');
    x=ch^'0';while((ch=gc)>='0'&&ch<='9')
        x=(x<<1)+(x<<3)+(ch^'0');return x;
}
struct segment{
    int l,r;lint s;
    vector<int> id;
    segment *ch[2];
}*rt;
inline int push_up(segment* &rt) { return rt->s=rt->ch[0]->s+rt->ch[1]->s,0; }
int build(segment* &rt,int l,int r)
{
    rt=new segment,rt->l=l,rt->r=r,rt->id.clear();
    if(l==r) return rt->s=a[l],0;int mid=(l+r)>>1;
    return build(rt->ch[0],l,mid),build(rt->ch[1],mid+1,r),push_up(rt);
}
int update(segment* &rt,int s,int t,int id)
{
    int l=rt->l,r=rt->r,mid=(l+r)>>1;
    if(s<=l&&r<=t) return rt->id.push_back(id),cnt[id]++,0;
    if(s<=mid) update(rt->ch[0],s,t,id);
    if(mid<t) update(rt->ch[1],s,t,id);
    return 0;
}
int update(segment* &rt,int p)
{
    int l=rt->l,r=rt->r,mid=(l+r)>>1;rt->s--;
    if(!rt->s&&rt->id.size())
    {
        Rep(i,rt->id) if(!(--cnt[rt->id[i]])) ans++;
        rt->id.clear();
    }
    if(l==r) return 0;
    return update(rt->ch[p>mid],p);
}
int main()
{
    int n=inn(),m=inn(),l,r;
    rep(i,1,n) a[i]=inn();build(rt,1,n);
    rep(i,1,m) l=inn(),r=inn(),update(rt,l,r,i);
    for(int q=inn(),las=0;q;q--)
        update(rt,(inn()+las-1)%n+1),printf("%d\n",las=ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Mys_C_K/article/details/82529012