[JZOJ3893] 画矩形

Description

有N个操作。

  • 在平面直角坐标系中画一个边与坐标轴平行的矩形,输入左下角点坐标和右上角点坐标。
  • 给出一个点坐标,查询被多少矩形覆盖。

所有点均在第一象限或X,Y轴的非负半轴上,N<=200000

Solution

经典的CDQ分治(整体二分)。
设矩形(x,y)(p,q)
一个矩形可以拆成四个点,(x,y),(x,q+1),(p+1,y),(p+1,q+1)
第一个点和第四个点权值为1,另两个为-1。
那么对于查询就是统计原点与这个点构成的矩形中的所有点权和(思考为什么?)

拆点以后,根据读入时间存起来。
显然只有左边的点对右边才有影响
二分X坐标,扫一遍对于 [L,mid] 的操作点就将它Y坐标打进树状数组,对于 [mid,R] 的查询点就用它的Y坐标在树状数组上查询

这样我们相当于将当前所有点分成了两部分,两个数组存起来分治下去,在mid上的点不用存。

因为坐标从0开始,可以先全部+1方便树状数组统计。
复杂度 O(NlogN)

Code

#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define N 600015
using namespace std;
int a[2*N][4],n,q,c[N],lim,ans[N],b[20][N][4],n1,le[20];
int lowbit(int k) 
{
    return k&(-k);
}
void put(int k,int v)
{
    while(k<=lim) c[k]+=v,k+=lowbit(k);
}
int get(int k)
{
    int s=0;
    while(k>0) s+=c[k],k-=lowbit(k);
    return s;
}
void turn(int p,int q,int w)
{
    le[p]++;
    fo(i,0,3) b[p][le[p]][i]=b[q][w][i];
}
void doit(int l,int r,int p)
{ 
    int mid=(l+r)/2;
    if(l>r) return;
    fo(i,1,le[p])
    {
        if(b[p][i][2]!=0&&b[p][i][0]<=mid) put(b[p][i][1],b[p][i][2]);
        if(b[p][i][2]==0&&b[p][i][0]>=mid) ans[b[p][i][3]]+=get(b[p][i][1]);
    }
    fo(i,1,le[p]) if(b[p][i][2]!=0&&b[p][i][0]<=mid) put(b[p][i][1],-b[p][i][2]);
    if(l<mid)
    {
        n1++,le[n1]=0;
        fo(i,1,le[p]) if(b[p][i][0]<mid) turn(n1,p,i); 
        doit(l,mid-1,n1);
        n1--;
    }
    if(mid<r)
    {
        n1++,le[n1]=0;
        fo(i,1,le[p]) if(b[p][i][0]>mid) turn(n1,p,i);
        doit(mid+1,r,n1);
        n1--;
    }
}
void nwp(int x,int y,int z,int t)
{
    b[1][++le[1]][0]=x;
    b[1][le[1]][1]=y;
    b[1][le[1]][2]=z;
    b[1][le[1]][3]=t;
}
int main()
{
    cin>>n;
    q=0;
    n1=1;
    fo(i,1,n)
    {
        int p,x,y,l=0,r;
        scanf("%d",&p);
        if(p==0) 
        {
            scanf("%d%d%d%d",&x,&y,&l,&r);
            x++,y++,l++,r++;
            nwp(x,y,1,i);
            nwp(l+1,y,-1,i);
            nwp(x,r+1,-1,i);
            nwp(l+1,r+1,1,i);
        }
        else scanf("%d%d",&x,&y),nwp(++x,++y,0,i);
        lim=max(lim,max(x,l+1));
    }
    doit(1,lim,1);
    fo(i,1,le[1])
    {
        if(b[1][i][2]==0) printf("%d\n",ans[b[1][i][3]]);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值