BZOJ 2683: 简单题(CDQ分治 + 树状数组)

题意

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

命令 参数限制 内容
1 x y A 1x,yN,A是正整数 将格子x,y里的数字加上A
2 x1 y1 x2 y2 1x1x2N,1y1y2N 输出x1 y1 x2 y2这个矩形内的数字和
3 终止程序

1<=N<=500000,操作数不超过200000个,内存限制20M

题解

这个题是 cdq分治 的裸题吧。

一维:时间(按输入顺序就行了)

二维:x坐标(cdq分治)

三维:y坐标(树状数组)

这个题比较裸,但是cdq分治细节还是有一点的(调的错误我可以列一版了。。)

算法讲解

但我想简单讲一下cdq分治(因为网上很多都很坑没讲清楚)

cdq是专门解决多维偏序的问题,比如像这一道题统计二维矩形的权值,或者直接求高维偏序的个数。

如果不用cdq分治,就只能树套树或者KD-tree这种巨型工业数据结构。而且树套树常常空间和常数都很恐怖,并且很难写……

cdq分治是个比较好写的东西,但其中的思想十分的巧妙和神奇。

你应该学过归并排序求逆序对吧,那是最裸的cdq了。他就是利用了左边的答案来更新右边的答案,cdq就是在这个方面不同于普通的分治。

它每次算答案,只能在右边区间算也就是[mid+1,r]。这是为什么可以这样呢,因为你初始给它的序列,按这样算的话,绝对只会算它原序列左边的贡献,不会算到右边去。(想一想,为什么) 这个只需要自己模拟下分治的区间划分和左右区间考虑就行了。

这就可以会强制使你一开始的那一维有序,对答案计算是正确的。(但切记最后给你的序列不一定是按你给它的顺序了!!!)

然后它中间会有一个排序比较的过程,这就可以使第二个维度变得有序了。(最后的序列一定是第二维度有序的) 然后根据前两个维度算答案就行了,后面的维度全都是附加在这两个维度上面的。

总的步骤:

  1. 分开(递归计算左区间和右区间)
  2. 计算(用左区间来统计,右区间来加上贡献)
  3. 合并(将当前序列变得有序)

又回到题解

这道题,就是对于所有操作进行cdq分治(一般都是对于操作进行分治)。

第三维用树状数组统计y的前缀和就行了,因为x已经排好序了,所以可以直接算了。

左区间只执行Add操作,右区间只执行Sum操作。

对于一个询问操作,要将它拆成4个询问操作(就是类似询问二维前缀和),加加减减就行了。

注意几个细节(我调了很久的点)

  1. 树状数组清空的时候,下标不是val而是y
  2. 拆矩阵的时候,一定要不要写错下标;

然后多拍几组,写个暴力很容易查出来的。

代码

#include <bits/stdc++.h> #define For(i, l, r) for(register int i = (l), _end_ = (int)(r); i <= _end_; ++i) #define Fordown(i, r, l) for(register int i = (r), _end_ = (int)(l); i >= _end_; --i) #define Set(a, v) memset(a, v, sizeof(a)) using namespace std; inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;} inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;} inline int read() { int x = 0, fh = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar() ) if (ch == '-') fh = -1; for (; isdigit(ch); ch = getchar() ) x = (x<<1) + (x<<3) + (ch ^ '0'); return x * fh; } void File() { #ifdef zjp_shadow freopen ("P2683.in", "r", stdin); freopen ("P2683.out", "w", stdout); #endif } int n; const int N = 800100; struct Opt { int x, y, type, id, val; inline bool operator < (const Opt &rhs) const { return (x ^ rhs.x) ? x < rhs.x : type < rhs.type; } }; Opt lt[N], tmp[N]; #define lowbit(x) (x & -(x)) struct Fenwick_Tree { int c[500100]; inline void Add(int pos, int val) { for (; pos <= n; pos += lowbit(pos) ) c[pos] += val; } inline int Sum(int pos) { int res = 0; for (; pos; pos -= lowbit(pos) ) res += c[pos]; return res; } inline void Clear(int pos) { for (; pos <= n; pos += lowbit(pos) ) if (c[pos]) c[pos] = 0; else break; } }; Fenwick_Tree T; int ans[N]; void Cdq(int l, int r) { if (l == r) return ; int mid = (l + r) >> 1; Cdq(l, mid); Cdq(mid + 1, r); int lp = l, rp = mid + 1, o = l; while (lp <= mid && rp <= r) { if (lt[lp] < lt[rp]) { if (lt[lp].type == 1) T.Add(lt[lp].y, lt[lp].val); tmp[o ++] = lt[lp ++]; } else { if (lt[rp].type == 2) ans[lt[rp].id] += lt[rp].val * T.Sum(lt[rp].y); tmp[o ++] = lt[rp ++]; } } while (lp <= mid) tmp[o ++] = lt[lp ++]; while (rp <= r) { if (lt[rp].type == 2) ans[lt[rp].id] += lt[rp].val * T.Sum(lt[rp].y); tmp[o ++] = lt[rp ++]; } For (i, l, mid) if (lt[i].type == 1) T.Clear(lt[i].y); For (i, l, r) lt[i] = tmp[i]; } int qcnt, acnt; inline void Addq(int x, int y, int type, int id, int val) { lt[++qcnt] = (Opt){x, y, type, id, val}; } int main () { File() ; n = read(); for (;;) { int opt = read(); if (opt == 3) break ; int xa, ya, xb, yb, val; if (opt == 1) { xa = read(); ya = read(); val = read(); Addq(xa, ya, 1, 0, val); } else { xa = read(); ya = read(); xb = read(); yb = read(); Addq(xa - 1, ya - 1, 2, (++ acnt), 1); Addq(xa - 1, yb, 2, acnt, -1); Addq(xb, ya - 1, 2, acnt, -1); Addq(xb, yb, 2, acnt, 1); } } Cdq(1, qcnt); For (i, 1, acnt) printf ("%d\n", ans[i]); return 0; }


__EOF__

本文作者zjp_shadow
本文链接https://www.cnblogs.com/zjp-shadow/p/8447545.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zjp_shadow  阅读(524)  评论(2编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示