【OJ1496】踩气球

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);
    }
}

猜你喜欢

转载自www.cnblogs.com/farway17/p/9347169.html
今日推荐