CodeChef QRECT: Rectangle Query 题解

博客介绍了如何解决CodeChef上的QRECT问题,关键在于转换为求不相交矩形的数量。通过容斥原理,结合树状数组处理完全包含的矩形,并利用CDQ分治和树状数组处理三维偏序问题,降低编码难度和常数,最终达到优化解决方案的目的。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这题的思路还是挺巧妙的
最关键的一步是我们改求与当前矩形不相交的矩形个数
那么显然可以容斥,用完全在上面的+下面的+左面的+右面的-四个角上被重复算的
前面的四个比较好搞,以上面举例,插入矩形时把矩形的下边界插入树状数组,查询时抓住当前矩形的上边界在树状数组里查就好了
四个角的部分,以左上角为例,插入矩形时把矩形的右下角记录下来,查询时查矩形的左上角的左上方有多少个点
所以现在相当于要实现在平面里面加点删点,然后求一个矩形区域内有多少个点
考虑到cc没有空间限制,刚开始无脑上了动态开点二维线段树,拼命卡常,把线段树换成非递归的又把外层线段树换成树桩才过

#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdlib>
#include <utility>
#include <cctype>
#include <algorithm>
#include <bitset>
#include <set>
#include <map>
#include <vector>
#include <queue>
#include <deque>
#include <stack>
#include <cmath>
#define LL long long
#define LB long double
#define x first
#define y second
#define Pair pair<int,int>
#define pb push_back
#define pf push_front
#define mp make_pair
#define LOWBIT(x) x & (-x)
using namespace std;

const int MOD=1e9+7;
const LL LINF=2e16;
const int INF=2e9;
const int magic=348;
const double eps=1e-10;
const double pi=acos(-1);

inline int getint()
{
    char ch;int res;bool f;
    while (!isdigit(ch=getchar()) && ch!='-') {}
    if (ch=='-') f=false,res=0; else f=true,res=ch-'0';
    while (isdigit(ch=getchar())) res=res*10+ch-'0';
    return f?res:-res;
}

struct BIT
{
    int c[400048];
    inline void init() {memset(c,0,sizeof(c));}
    inline void update(int x,int lim,int delta) {while (x<=lim) c[x]+=delta,x+=LOWBIT(x);}
    inline int query(int x) {int res=0;while (x) res+=c[x],x-=LOWBIT(x);return res;}
}up,down,lft,rght;

int n,itot1,itot2;
int ind[100048],indtot=0;

struct Query
{
    int type;
    int x1,y1,x2,y2,t;
    int nx1,ny1,nx2,ny2;
}a[100048];

struct point
{
    int val;
    int from;bool type;
    inline bool operator < (const point &x) const {return val<x.val;}
}b[500048];int btot;

namespace SegmentTree
{
    int tree[5][(330+5)*200048];
    int lson[(330+5)*200048],rson[(330+5)*200048];int tot=0;
    int lis[100048],litot=0;
    inline void updatey(int cur,int type,int pos,int delta,int l,int r)
    {
        litot=0;
        while (l<=r)
        {
            lis[++litot]=cur;
            if (l==r) break;
            int mid=(l+r)>>1;
            if (pos<=mid)
            {
                if (!lson[cur]) lson[cur]=++tot;
                cur=lson[cur];r=mid;
            }
            else
            {
                if (!rson[cur]) rson[cur]=++tot;
                cur=rson[cur];l=mid+1;
            }
        }
        for (register int i=litot;i>=1;i--) tree[type][lis[i]]+=delta;
    }
    int List[100048],ltot=0;
    inline void updatex(int type,int x,int y,int delta,int r)
    {
        while (x<=r)
        {
            updatey(x,type,y,delta,1,itot2);
            x+=LOWBIT(x);
        }
    }
    int qy[5000048],qyhead,qytail;
    inline int queryy(int cur,int type,int left,int right,int l,int r)
    {
        qyhead=1;qytail=3;qy[1]=l;qy[2]=r;qy[3]=cur;int res=0,curleft,curright,cc,mid;
        while (qyhead<=qytail)
        {
            curleft=qy[qyhead++];curright=qy[qyhead++];cc=qy[qyhead++];
            if (left<=curleft && curright<=right) {res+=tree[type][cc];continue;}
            mid=(curleft+curright)>>1;
            if (left<=mid) qy[++qytail]=curleft,qy[++qytail]=mid,qy[++qytail]=lson[cc];
            if (mid+1<=right) qy[++qytail]=mid+1,qy[++qytail]=curright,qy[++qytail]=rson[cc];
        }
        return res;
    }
    inline int Query(int x,int y1,int y2,int type)
    {
        int res=0;
        while (x)
        {
            res+=queryy(x,type,y1,y2,1,itot2);
            x-=LOWBIT(x);
        }
        return res;
    }
    inline int queryx(int type,int x1,int x2,int y1,int y2)
    {
        if (x1>x2 || y1>y2) return 0;
        return Query(x2,y1,y2,type)-Query(x1-1,y1,y2,type);
    }
}

int main ()
{
    freopen ("a.in","r",stdin);
    freopen ("a.ans","w",stdout);
    int i;n=getint();string type;
    for (i=1;i<=n;i++)
    {
        cin>>type;
        if (type=="I") a[i].type=1;
        if (type=="D") a[i].type=2;
        if (type=="Q") a[i].type=3;
        if (a[i].type==1 || a[i].type==3) a[i].x1=getint(),a[i].y1=getint(),a[i].x2=getint(),a[i].y2=getint();
        else a[i].t=getint();
    }
    btot=0;
    for (i=1;i<=n;i++)
        if (a[i].type!=2) b[++btot]=point{a[i].x1,i,false},b[++btot]=point{a[i].x2,i,true};
    sort(b+1,b+btot+1);itot1=0;
    for (i=1;i<=btot;i++)
    {
        if (i==1 || b[i].val!=b[i-1].val) itot1++;
        if (!b[i].type) a[b[i].from].nx1=itot1; else a[b[i].from].nx2=itot1;
    }
    btot=0;
    for (i=1;i<=n;i++)
        if (a[i].type!=2) b[++btot]=point{a[i].y1,i,false},b[++btot]=point{a[i].y2,i,true};
    sort(b+1,b+btot+1);itot2=0;
    for (i=1;i<=btot;i++)
    {
        if (i==1 || b[i].val!=b[i-1].val) itot2++;
        if (!b[i].type) a[b[i].from].ny1=itot2; else a[b[i].from].ny2=itot2;
    }
    int pt=0,cnt=0,exist=0;
    up.init();down.init();lft.init();rght.init();
    SegmentTree::tot=itot1*4;
    for (i=1;i<=n;i++)
    {
        if (a[i].type==1)
        {
            exist++;ind[++indtot]=i;
            down.update(a[i].ny2,itot2,1);
            up.update(itot2-a[i].ny1+1,itot2,1);
            lft.update(a[i].nx2,itot1,1);
            rght.update(itot1-a[i].nx1+1,itot1,1);
            SegmentTree::updatex(1,a[i].nx2,a[i].ny1,1,itot1);
            SegmentTree::updatex(2,a[i].nx1,a[i].ny1,1,itot1);
            SegmentTree::updatex(3,a[i].nx2,a[i].ny2,1,itot1);
            SegmentTree::updatex(4,a[i].nx1,a[i].ny2,1,itot1);
        }
        if (a[i].type==2)
        {
            exist--;pt=ind[a[i].t];
            down.update(a[pt].ny2,itot2,-1);
            up.update(itot2-a[pt].ny1+1,itot2,-1);
            lft.update(a[pt].nx2,itot1,-1);
            rght.update(itot1-a[pt].nx1+1,itot1,-1);
            SegmentTree::updatex(1,a[pt].nx2,a[pt].ny1,-1,itot1);
            SegmentTree::updatex(2,a[pt].nx1,a[pt].ny1,-1,itot1);
            SegmentTree::updatex(3,a[pt].nx2,a[pt].ny2,-1,itot1);
            SegmentTree::updatex(4,a[pt].nx1,a[pt].ny2,-1,itot1);
        }
        if (a[i].type==3)
        {
            int curcnt=0;
            curcnt+=down.query(a[i].ny1-1);
            curcnt+=up.query(itot2-a[i].ny2);
            curcnt+=lft.query(a[i].nx1-1);
            curcnt+=rght.query(itot1-a[i].nx2);
            curcnt-=SegmentTree::queryx(1,1,a[i].nx1-1,a[i].ny2+1,itot2);
            curcnt-=SegmentTree::queryx(2,a[i].nx2+1,itot1,a[i].ny2+1,itot2);
            curcnt-=SegmentTree::queryx(3,1,a[i].nx1-1,1,a[i].ny1-1);
            curcnt-=SegmentTree::queryx(4,a[i].nx2+1,itot1,1,a[i].ny1-1);
            printf("%d\n",exist-curcnt);
        }
    }
    return 0;
}

后来发现这个东西其实就是一个三维偏序,时间是第一维,坐标是二三维,所以可以用cdq分治+树状数组来搞,时间复杂度一样但是编码难度和常数都很优秀

#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdlib>
#include <utility>
#include <cctype>
#include <algorithm>
#include <bitset>
#include <set>
#include <map>
#include <vector>
#include <queue>
#include <deque>
#include <stack>
#include <cmath>
#define LL long long
#define LB long double
#define x first
#define y second
#define Pair pair<int,int>
#define pb push_back
#define pf push_front
#define mp make_pair
#define LOWBIT(x) x & (-x)
using namespace std;

const int MOD=1e9+7;
const LL LINF=2e16;
const int INF=2e9;
const int magic=348;
const double eps=1e-10;
const double pi=acos(-1);

inline int getint()
{
    char ch;int res;bool f;
    while (!isdigit(ch=getchar()) && ch!='-') {}
    if (ch=='-') f=false,res=0; else f=true,res=ch-'0';
    while (isdigit(ch=getchar())) res=res*10+ch-'0';
    return f?res:-res;
}

struct BIT
{
    int c[400048];
    inline void init() {memset(c,0,sizeof(c));}
    inline void update(int x,int lim,int delta) {while (x<=lim) c[x]+=delta,x+=LOWBIT(x);}
    inline void clear(int x,int lim) {while (x<=lim) c[x]=0,x+=LOWBIT(x);}
    inline int query(int x) {int res=0;while (x) res+=c[x],x-=LOWBIT(x);return res;}
}up,down,lft,rght,CDQ;

struct Query
{
    int type;
    int x1,y1,x2,y2;
    int nx1,nx2,ny1,ny2;
}a[100048];

struct C
{
    int type;
    int x,y,ind;
}c[100048],tmp[100048];

struct point
{
    int val;
    int from;bool type;
    inline bool operator < (const point &x) const {return val<x.val;}
}b[500048];int btot;

int n,itot1,itot2;
int ind[100048],indtot=0;
int ans[100048];
int history[100048],htot;

inline void cdq(int left,int right)
{
    if (left==right) return;
    int mid=(left+right)>>1,k1,k2,pt;
    cdq(left,mid);cdq(mid+1,right);htot=0;
    for (k1=left,k2=mid+1,pt=left;k1<=mid && k2<=right;)
    {
        if (c[k1].x<=c[k2].x)
        {
            if (c[k1].type==1) CDQ.update(c[k1].y,itot2,1),history[++htot]=c[k1].y;
            if (c[k1].type==2) CDQ.update(c[k1].y,itot2,-1),history[++htot]=c[k1].y;
            tmp[pt++]=c[k1++];
        }
        else
        {
            if (c[k2].type==3) ans[c[k2].ind]+=CDQ.query(c[k2].y);
            tmp[pt++]=c[k2++];
        }
    }
    while (k1<=mid) tmp[pt++]=c[k1++];
    while (k2<=right)
    {
        if (c[k2].type==3) ans[c[k2].ind]+=CDQ.query(c[k2].y);
        tmp[pt++]=c[k2++];
    }
    for (register int i=left;i<=right;i++) c[i]=tmp[i];
    for (register int i=1;i<=htot;i++) CDQ.clear(history[i],itot2);
}

int main ()
{
    //freopen ("a.in","r",stdin);
    //freopen ("a.out","w",stdout);
    int i,x;n=getint();string type;
    for (i=1;i<=n;i++)
    {
        cin>>type;
        if (type=="I")
        {
            ind[++indtot]=i;
            a[i].x1=getint();a[i].y1=getint();a[i].x2=getint();a[i].y2=getint();
            a[i].type=1;
        }
        if (type=="D")
        {
            x=getint();
            a[i]=a[ind[x]];a[i].type=2;
        }
        if (type=="Q")
        {
            a[i].x1=getint();a[i].y1=getint();a[i].x2=getint();a[i].y2=getint();
            a[i].type=3;
        }
    }
    btot=0;
    for (i=1;i<=n;i++) b[++btot]=point{a[i].x1,i,false},b[++btot]=point{a[i].x2,i,true};
    sort(b+1,b+btot+1);itot1=0;
    for (i=1;i<=btot;i++)
    {
        if (i==1 || b[i-1].val!=b[i].val) itot1++;
        if (!b[i].type) a[b[i].from].nx1=itot1; else a[b[i].from].nx2=itot1;
    }
    btot=0;
    for (i=1;i<=n;i++) b[++btot]=point{a[i].y1,i,false},b[++btot]=point{a[i].y2,i,true};
    sort(b+1,b+btot+1);itot2=0;
    for (i=1;i<=btot;i++)
    {
        if (i==1 || b[i-1].val!=b[i].val) itot2++;
        if (!b[i].type) a[b[i].from].ny1=itot2; else a[b[i].from].ny2=itot2;
    }
    up.init();down.init();lft.init();rght.init();
    int exist=0;
    for (i=1;i<=n;i++)
    {
        if (a[i].type==1)
        {
            exist++;
            down.update(a[i].ny2,itot2,1);
            up.update(itot2-a[i].ny1+1,itot2,1);
            lft.update(a[i].nx2,itot1,1);
            rght.update(itot1-a[i].nx1+1,itot1,1);
        }
        if (a[i].type==2)
        {
            exist--;
            down.update(a[i].ny2,itot2,-1);
            up.update(itot2-a[i].ny1+1,itot2,-1);
            lft.update(a[i].nx2,itot1,-1);
            rght.update(itot1-a[i].nx1+1,itot1,-1);
        }
        if (a[i].type==3)
        {
            ans[i]=exist;
            ans[i]-=down.query(a[i].ny1-1);
            ans[i]-=up.query(itot2-a[i].ny2);
            ans[i]-=lft.query(a[i].nx1-1);
            ans[i]-=rght.query(itot1-a[i].nx2);
        }
    }
    //up-left
    for (i=1;i<=n;i++)
    {
        if (a[i].type==1 || a[i].type==2) c[i]=C{a[i].type,a[i].nx2,itot2-a[i].ny1+1}; else c[i]=C{3,a[i].nx1-1,itot2-a[i].ny2};
        c[i].ind=i;
    }
    cdq(1,n);
    //up-right
    for (i=1;i<=n;i++)
    {
        if (a[i].type==1 || a[i].type==2) c[i]=C{a[i].type,itot1-a[i].nx1+1,itot2-a[i].ny1+1};
        else c[i]=C{3,itot1-a[i].nx2,itot2-a[i].ny2};
        c[i].ind=i;
    }
    cdq(1,n);
    //down-left
    for (i=1;i<=n;i++)
    {
        if (a[i].type==1 || a[i].type==2) c[i]=C{a[i].type,a[i].nx2,a[i].ny2};
        else c[i]=C{3,a[i].nx1-1,a[i].ny1-1};
        c[i].ind=i;
    }
    cdq(1,n);
    //down-right
    for (i=1;i<=n;i++)
    {
        if (a[i].type==1 || a[i].type==2) c[i]=C{a[i].type,itot1-a[i].nx1+1,a[i].ny2};
        else c[i]=C{3,itot1-a[i].nx2,a[i].ny1-1};
        c[i].ind=i;
    }
    cdq(1,n);
    for (i=1;i<=n;i++)
        if (a[i].type==3) printf("%d\n",ans[i]);
    return 0;
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值