ACdream 1157 Segments(CDQ分治)

Description
由3种类型操作:
1)D L R(1 <= L <= R <= 1000000000) 增加一条线段[L,R]
2)C i (1-base) 删除第i条增加的线段,保证每条插入线段最多插入一次,且这次删除操作一定合法
3) Q L R(1 <= L <= R <= 1000000000) 查询目前存在的线段中有多少条线段完全包含[L,R]这个线段,线段X被线段Y完全包含即LY <= LX
<= RX <= RY)
Input
多组数据,每组数据N
接下来N行,每行是三种操作之一(1 <= N <= 10^5)
Output
对于每个Q操作,输出一行,答案
Sample Input
6
D 1 100
D 3 8
D 4 10
Q 3 8
C 1
Q 3 8
Sample Output
2
1
Solution
CDQ分治,将删除操作看作插入一条数量为-1的线段,查询操作看作插入一条数量为0的线段,用cnt[i]表示第i次插入的线段被之前插入的线段包含的次数,按操作顺序进行分治,每次统计[l,mid+1]中有多少元素j满足j.y>=i.y,j.x<=i.x,其中mid+1<=i<=r,这个过程可以通过对两个区间都以x为第一关键字降序排,以y为第二关键字升序排,对于[mid+1,r]中的每个i(i为数量为0的元素,即为查询),将[l,mid]中所有满足j.y>=i.y的j以j.x为下标,cnt[j]为键值插入到树状数组中,那么每次只需统计树状数组中下标小于等于i.x的元素键值之和累加到cnt[i]中即可
Code

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
#define maxn 222222
int n,h[maxn],tot;
struct node
{
    int x,y,cnt,id,ans;
    bool operator <(const node &a)const
    {
        if(y!=a.y)return y>a.y;
        return x<a.x;
    }
}p[maxn],q[maxn];
int cmpid(node a,node b)
{
    return a.id<b.id;
}
struct BIT 
{
    #define lowbit(x) (x&(-x))
    int b[maxn];
    void init()
    {
        memset(b,0,sizeof(b));
    }
    void update(int x,int v)
    {
        while(x<=tot)
        {
            b[x]+=v;
            x+=lowbit(x);
        }
    }
    int query(int x)
    {
        int ans=0;
        while(x)
        {
            ans+=b[x];
            x-=lowbit(x);
        }
        return ans;
    }
}bit;
void CDQ(int l,int r)
{
    if(l==r)return ;
    int mid=(l+r)>>1; 
    CDQ(l,mid);
    CDQ(mid+1,r);
    sort(p+l,p+mid+1);
    sort(p+mid+1,p+r+1);
    int j=l;
    for(int i=mid+1;i<=r;i++)
    {
        for(;j<=mid&&p[j].y>=p[i].y;j++)
            bit.update(p[j].x,p[j].cnt);
        if(!p[i].cnt)p[i].ans+=bit.query(p[i].x);
    }
    for(int i=l;i<j;i++)bit.update(p[i].x,-p[i].cnt);
    merge(p+l,p+mid+1,p+mid+1,p+r+1,q);
    for(int i=0;i<r-l+1;i++)p[l+i]=q[i];;
}
int res,l[maxn],r[maxn];
int main()
{
    while(~scanf("%d",&n))
    {
        bit.init();
        res=1,tot=0;
        for(int i=1,j=0;i<=n;i++)
        {
            p[i].id=i,p[i].ans=0;
            char s[3];int temp; 
            scanf("%s",s);
            if(s[0]=='D')
            {
                scanf("%d%d",&p[i].x,&p[i].y);
                p[i].cnt=1;
                l[res]=p[i].x,r[res++]=p[i].y;
                h[++tot]=p[i].x,h[++tot]=p[i].y;
            }
            else if(s[0]=='Q')
            {
                scanf("%d%d",&p[i].x,&p[i].y);
                p[i].cnt=0;
                h[++tot]=p[i].x,h[++tot]=p[i].y;
            }
            else 
            {
                scanf("%d",&temp);
                p[i].x=l[temp],p[i].y=r[temp];
                p[i].cnt=-1;
            }
        }
        sort(h+1,h+tot+1);
        tot=unique(h+1,h+tot+1)-h-1;
        for(int i=1;i<=n;i++)
        {
            p[i].x=lower_bound(h+1,h+tot+1,p[i].x)-h;
            p[i].y=lower_bound(h+1,h+tot+1,p[i].y)-h;
        }   
        CDQ(1,n);
        sort(p+1,p+n+1,cmpid);
        for(int i=1;i<=n;i++)
            if(!p[i].cnt)printf("%d\n",p[i].ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值