[树状数组]BZOJ 2028——[SHOI2009]会场预约

题目梗概

有两种操作:

一种是插入一段区间,并删除与这段区间相交的区间,返回删除区间的个数。

另一种是返回目前的区间数。

解题思路

有一个非常重要的特性是在任何时候区间的末端随区间的始端递增而递增。

于是考虑树状数组维护始端个数的前缀和,维护这个就可以二分查找小于等于某个点最近的始端。

知道始端的位置后,我们可以得到相应的末端,如果形成相交就删去这个区间(其实就是删去始端)。

因为始末端的单调性,当查找出的区间不形成相交就可以停止了。

因为每个区间最多被删去一次,所以效率为 O(Nlog2N)

#include<cstdio>
using namespace std;
const int N=100000;
inline int _read(){
    int num=0;char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') num=num*10+ch-48,ch=getchar();
    return num;
}
int n,a[N+5],r[N+5];
int lowbit(int x){return x&(-x);}
void add(int x,int y){for (;x<=N;x+=lowbit(x)) a[x]+=y;}
int ask(int x){int num=0;for (;x;x-=lowbit(x)) num+=a[x];return num;}
int main(){
    freopen("exam.in","r",stdin);
    freopen("exam.out","w",stdout);
    n=_read();
    for (int i=1;i<=n;i++){
        char ch=getchar();
        while(ch!='A'&&ch!='B') ch=getchar();
        if (ch=='B') printf("%d\n",ask(N));else{
            int x=_read(),y=_read(),tot=0;
            while(1){
                int L=1,R=y,mid,w=ask(y),h;
                if (!w) break;
                while(L<=R){
                    int mid=L+(R-L>>1);
                    if (ask(mid)>=w) h=mid,R=mid-1;else L=mid+1;
                }
                if (r[h]>=x) add(h,-1),tot++;else break;
            }
            add(x,1);r[x]=y;
            printf("%d\n",tot);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值