[BZOJ4066]简单题(KD-tree)

本文介绍了一个利用二维树状数组解决的问题,并讨论了如何通过CDQ分治来处理该问题。文章详细解释了如何构建和维护一棵平衡的树结构,以及查询过程中类似于线段树的操作。

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

题目:

我是超链接

题解:

喵喵喵一看题:这不是二维树状数组吗???然后一看数据范围N<=500000。。。。。
【其实这个题如果不是强制在线也可以用cdq分治做的】
这里有一个二维平面,我们就可以以点为节点组成一棵树!
本来想的是对于每一个要添加数字的点去添加数字,天哪那要好麻烦,还要记录有没有这个点,给每一个点加一个编号。。。我们可以把每一次添加当做添加一个新点,大不了就是一个重点,没有什么问题
为了保证效率,因为我们一开始建的树并不是一棵优美的KD-tree,所以不隔多久我们要重建一次,保证树的平衡性
至于qurry的过程思想上类似线段树,这一段在范围内就全部加入,不然就继续走分支
编码过程及其艰难,经过排查竟然有两处错误,一处手残,另一处是rebuild函数里一定要先把t[mid].l,t[mid].r设为0,因为ta都不一定有这棵子树了,后来肯定会错啊

代码:

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
struct hh{int d[2],l,r,mn[2],mx[2],sum,zz;}t[200005];
int cmpd,ans=0,x1,x2,y1,y2,root;
int cmp(const hh &a,const hh &b)
{
    return (a.d[cmpd]<b.d[cmpd] || (a.d[cmpd]==b.d[cmpd] && a.d[!cmpd]<b.d[!cmpd]));
}
void updata(int now)
{
    int lc=t[now].l,rc=t[now].r;
    if (lc)
    {
        t[now].mn[0]=min(t[now].mn[0],t[lc].mn[0]);
        t[now].mn[1]=min(t[now].mn[1],t[lc].mn[1]);
        t[now].mx[0]=max(t[now].mx[0],t[lc].mx[0]);
        t[now].mx[1]=max(t[now].mx[1],t[lc].mx[1]);
    }
    if (rc)
    {
        t[now].mn[0]=min(t[now].mn[0],t[rc].mn[0]);
        t[now].mn[1]=min(t[now].mn[1],t[rc].mn[1]);
        t[now].mx[0]=max(t[now].mx[0],t[rc].mx[0]);
        t[now].mx[1]=max(t[now].mx[1],t[rc].mx[1]);
    }
    t[now].sum=t[lc].sum+t[rc].sum+t[now].zz;
}
int rebuild(int l,int r,int D)
{
    cmpd=D;
    int mid=(l+r)>>1;
    nth_element(t+l+1,t+mid+1,t+r+1,cmp);
    t[mid].mn[0]=t[mid].mx[0]=t[mid].d[0];
    t[mid].mn[1]=t[mid].mx[1]=t[mid].d[1];
    t[mid].sum=t[mid].zz;
    t[mid].l=t[mid].r=0;       ////
    if (l!=mid) t[mid].l=rebuild(l,mid-1,!D);
    if (r!=mid) t[mid].r=rebuild(mid+1,r,!D);
    updata(mid); return mid;
}
void insert(int now)
{
    if (!root) {root=now; return;}
    int p=root,D=0;
    while (1)
    {
        if (t[now].mn[0]<t[p].mn[0]) t[p].mn[0]=t[now].mn[0];
        if (t[now].mn[1]<t[p].mn[1]) t[p].mn[1]=t[now].mn[1];
        if (t[now].mx[0]>t[p].mx[0]) t[p].mx[0]=t[now].mx[0];
        if (t[now].mx[1]>t[p].mx[1]) t[p].mx[1]=t[now].mx[1];
        t[p].sum+=t[now].sum;
        if (t[now].d[D]<=t[p].d[D])
        {
            if (!t[p].l) {t[p].l=now; break;}
            else p=t[p].l;
        }
        else
        {
            if (!t[p].r) {t[p].r=now; break;}
            else p=t[p].r;
        }
        D=!D;
    }
}
bool ok(int now)
{
    return (t[now].d[0]>=x1 && t[now].d[1]>=y1 && t[now].d[0]<=x2 && t[now].d[1]<=y2);
}
int check(int now)//1-可以全部囊括,0-囊括部分,-1-无交集 
{
    if (t[now].mn[0]>=x1 && t[now].mn[1]>=y1 && t[now].mx[0]<=x2 && t[now].mx[1]<=y2) return 1;
    if (t[now].mn[0]>x2 || t[now].mn[1]>y2 || t[now].mx[0]<x1 || t[now].mx[1]<y1) return -1;
    return 0;
}
void qurry(int now)
{
    if (ok(now)) ans+=t[now].zz;
    if (t[now].l)
    {
        int bh=check(t[now].l);
        if (bh==1) ans+=t[t[now].l].sum;
        else if (bh==0) qurry(t[now].l);
    }
    if (t[now].r)
    {
        int bh=check(t[now].r);
        if (bh==1) ans+=t[t[now].r].sum;
        else if (bh==0) qurry(t[now].r);
    }
}
int main()
{
    int n,id,x,y,v;
    scanf("%d",&n);n=0;
    while (scanf("%d",&id))
    {
        if (id==3) break;
        if (id==1)
        {
            scanf("%d%d%d",&x,&y,&v);
            x^=ans; y^=ans; v^=ans;
            n++;
            t[n].mn[0]=t[n].mx[0]=t[n].d[0]=x; 
            t[n].mn[1]=t[n].mx[1]=t[n].d[1]=y;
            t[n].zz=t[n].sum=v; 
            insert(n);
            if (n%10000==0) root=rebuild(1,n,0);
        }
        if (id==2)
        {
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
            x1^=ans; y1^=ans; x2^=ans; y2^=ans;
            ans=0;
            qurry(root);
            printf("%d\n",ans);
        } 
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值