[Luogu P4148] [BZOJ 4066] 简单题

本文介绍使用KD-Tree解决特定区间查询与更新问题的方法。针对大规模数据集,通过KD-Tree实现高效的区间求和操作,并详细展示了如何通过重构树来优化内存使用及查询效率。

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

洛谷传送门
BZOJ传送门

题目描述

你有一个 N×N N × N 的棋盘,每个格子内有一个整数,初始时的时候全部为 0 0 ,现在需要维护两种操作:

  • 1 x y A 1x,yN A A 是正整数。将格子x,y里的数字加上 A

    • 2 x1 y1 x2 y2 1x1x2N 1 ≤ x 1 ≤ x 2 ≤ N 1y1y2N 1 ≤ y 1 ≤ y 2 ≤ N 。输出 x1,y1,x2,y2 x 1 , y 1 , x 2 , y 2 这个矩形内的数字和
    • 3 无 终止程序
    • 输入输出格式

      输入格式:

      输入文件第一行一个正整数 N N

      接下来每行一个操作。每条命令除第一个数字之外,均要异或上一次输出的答案last_ans,初始时last_ans=0

      输出格式:

      对于每个 2 2 操作,输出一个对应的答案。

      输入输出样例

      输入样例#1:

      4
      1 2 3 3
      2 1 1 3 3
      1 1 1 1
      2 1 1 0 7
      3

      输出样例#1:

      3
      5

      说明

      1N500000,操作数不超过 200000 200000 个,内存限制 20M 20 M ,保证答案在 int i n t 范围内并且解码之后数据仍合法。

      解题分析

      本来可以用 CDQ C D Q 或者树套树水过去的, 然而又卡内存又强制在线…只好 KDTree K D − T r e e

      因为支持插入操作, 和 BST B S T 一样插入后可能退化为链, 于是我们模仿替罪羊树的写法部分重构就好了。

      操作不超过 200000 200000 , 所以开 200000 200000 的数组即可。

      因为要部分重构, 所以需要开内存池。

      细节见下:

      #include <cstdio>
      #include <cmath>
      #include <cstring>
      #include <cctype>
      #include <cstdlib>
      #include <algorithm>
      #define R register
      #define IN inline
      #define gc getchar()
      #define W while
      #define MX 200050
      #define alpha 0.75
      template <class T>
      IN void in(T &x)
      {
          x = 0; R char c = gc;
          for (; !isdigit(c); c = gc);
          for (;  isdigit(c); c = gc)
          x = (x << 1) + (x << 3) + c - 48;
      }
      int dot, q, dim, top, root, mnx, mxx, mny, mxy;
      struct Point {int cood[2], val;} pt[MX];
      struct Node {int son[2], lim[2][2], siz, sum; Point info;} tree[MX];
      IN bool operator < (const Point &x, const Point &y)
      {return x.cood[dim] == y.cood[dim] ? x.cood[dim ^ 1] < y.cood[dim ^ 1] : x.cood[dim] < y.cood[dim];}
      int pool[MX];
      namespace KDT
      {
          #define ls tree[now].son[0]
          #define rs tree[now].son[1]
          IN int nw() {return pool[top--];}
          IN void pushup(R int now)
          {
              tree[now].sum = tree[now].info.val; tree[now].siz = 1;
              tree[now].lim[0][0] = tree[now].lim[0][1] = tree[now].info.cood[0];
              tree[now].lim[1][0] = tree[now].lim[1][1] = tree[now].info.cood[1];
              if(ls)
              {
                  tree[now].lim[0][0] = std::min(tree[now].lim[0][0], tree[ls].lim[0][0]);
                  tree[now].lim[0][1] = std::max(tree[now].lim[0][1], tree[ls].lim[0][1]);
                  tree[now].lim[1][0] = std::min(tree[now].lim[1][0], tree[ls].lim[1][0]);
                  tree[now].lim[1][1] = std::max(tree[now].lim[1][1], tree[ls].lim[1][1]);
                  tree[now].sum += tree[ls].sum; tree[now].siz += tree[ls].siz;
              }
              if(rs)
              {
                  tree[now].lim[0][0] = std::min(tree[now].lim[0][0], tree[rs].lim[0][0]);
                  tree[now].lim[0][1] = std::max(tree[now].lim[0][1], tree[rs].lim[0][1]);
                  tree[now].lim[1][0] = std::min(tree[now].lim[1][0], tree[rs].lim[1][0]);
                  tree[now].lim[1][1] = std::max(tree[now].lim[1][1], tree[rs].lim[1][1]);
                  tree[now].sum += tree[rs].sum; tree[now].siz += tree[rs].siz;
              }
          }
          int build(R int lef, R int rig, R int dm)//重建
          {
              if(lef > rig) return 0;
              dim = dm; int mid = lef + rig >> 1; int now = nw();
              std::nth_element(pt + lef, pt + mid, pt + rig + 1);
              tree[now].info = pt[mid];
              ls = build(lef, mid - 1, dm ^ 1);
              rs = build(mid + 1, rig, dm ^ 1);
              pushup(now); return now;
          }
          void crash(R int now, R int ord)//将节点压为序列, 保存点的信息
          {
              if(ls) crash(ls, ord);
              pt[tree[ls].siz + ord + 1] = tree[now].info, pool[++top] = now;//回收节点
              if(rs) crash(rs, ord + 1 + tree[ls].siz);
          }
          IN void isok(int &now, R int dm)//判定是否需要压扁
          {//因为涉及重构, 要传引用
              if(tree[ls].siz > tree[now].siz * alpha || tree[rs].siz > tree[now].siz * alpha)
              crash(now, 0), now = build(1, tree[now].siz, dm);
          }
          void insert(int &now, const Point &tar, R int dm)
          {
              if(!now)
              {
                  now = nw(); tree[now].info = tar; ls = rs = 0;
                  pushup(now); return;
              }
              dim = dm;
              if(tar.cood[dm] <= tree[now].info.cood[dm]) insert(ls, tar, dm ^ 1);
              else insert(rs, tar, dm ^ 1);
              pushup(now), isok(now, dm);
          }
          IN bool init(R int xd, R int xu, R int yd, R int yu)//完全在内部
          {return (xd >= mnx && xu <= mxx && yd >= mny && yu <= mxy);}
          IN bool outit(R int xd, R int xu, R int yd, R int yu)//完全在外部
          {return (xd > mxx || xu < mnx || yd > mxy || yu < mny);}
          int query(R int now)
          {
              if(!now) return 0;
              int ret = 0;
              if (init(tree[now].lim[0][0], tree[now].lim[0][1], tree[now].lim[1][0], tree[now].lim[1][1])) return tree[now].sum;
              if (outit(tree[now].lim[0][0], tree[now].lim[0][1], tree[now].lim[1][0], tree[now].lim[1][1])) return 0;
              if (init(tree[now].info.cood[0], tree[now].info.cood[0], tree[now].info.cood[1], tree[now].info.cood[1])) ret += tree[now].info.val;
              return ret + query(ls) + query(rs);
          }
          #undef ls
          #undef rs
      }
      int main(void)
      {
          int typ, a, b, c, last = 0; Point cur;
          in(dot); int rock_du_orz = 666;
          for (R int i = 1; i <= 200000; ++i) pool[i] = i; top = 200000;
          W (rock_du_orz)
          {
              in(typ);
              if(typ == 1) in(a), in(b), in(c), cur.cood[0] = a ^ last, cur.cood[1] = b ^ last, cur.val = c ^ last, KDT::insert(root, cur, 0);
              else if(typ == 2)
              {
                  in(mnx), in(mny), in(mxx), in(mxy);
                  mnx ^= last, mny ^= last, mxx ^= last, mxy ^= last;
                  if(mnx > mxx) std::swap(mnx, mxx);
                  if(mny > mxy) std::swap(mny, mxy);
                  printf("%d\n", last = KDT::query(root));
              }
              else break;
          }
      }
      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值