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

BZOJ2683: 简单题(CDQ分治 + 树状数组)
  • 题意:

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

命令 参数限制 内容
\(1\ x\ y\ A\) \(1\le x,y \le N\),A是正整数 将格子\(x,y\)里的数字加上\(A\)
\(2\ x1\ y1\ x2\ y2\) \(1\le x1\le x2\le N,1\le y1\le y2\le N\) 输出\(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;
}

?

原文地址:https://www.cnblogs.com/zjp-shadow/p/8447545.html

时间: 2024-08-25 05:18:37

BZOJ2683: 简单题(CDQ分治 + 树状数组)的相关文章

BZOJ 2683 简单题 cdq分治+树状数组

题意:链接 **方法:**cdq分治+树状数组 解析: 首先对于这道题,看了范围之后,二维的数据结构是显然不能过的,于是我们可能会考虑把一维排序之后另一位上数据结构什么的,然而cdq分治却能够很好的体现它的作用. 首先,对于每一个询问求和,显然是x在它左边的并且出现时间在它之前的所有的change对他可能会有影响. 我们按照x第一关键字,y第二关键字,操作第三关键字来排序所有的询问,然后在cdq的时候,每次递归处理左半区间,按照x动态的将y这一列的值加到树状数组里,来更新右半边的所有询问,注意这

HDU 5618:Jam&#39;s problem again(CDQ分治+树状数组处理三维偏序)

http://acm.hdu.edu.cn/showproblem.php?pid=5618 题意:-- 思路:和NEUOJ那题一样的.重新写了遍理解了一下,算作处理三维偏序的模板了. 1 #include <cstdio> 2 #include <algorithm> 3 #include <iostream> 4 #include <cstring> 5 using namespace std; 6 #define INF 0x3f3f3f3f 7 #d

XJOI NOIP2015模拟赛Day1 T2 ctps bitset优化 或 排序+cdq分治+树状数组+平衡树

题意: 4维空间中有1个点集A,|A|=n,用(a,b,c,d)表示每个点. 共有m个询问,每次询问输入一个点(a,b,c,d),求最大的S,其中S={p|p∈A且ap<=a,bp<=b,cp<=c,dp<=d},输出|S| 输入格式: 第一行n 接下来n行有n个4维点对 第n+2行有一个数m 再接下来m行每行有一个四维点对,表示每个询问 输出格式: 对于每个询问输出一个数 **方法:**bitset优化 或 排序+cdq分治+树状数组+平衡树 解析: 神题,考场不会,暴力骗40,

【BZOJ4553】[Tjoi2016&amp;Heoi2016]序列 cdq分治+树状数组

[BZOJ4553][Tjoi2016&Heoi2016]序列 Description 佳媛姐姐过生日的时候,她的小伙伴从某宝上买了一个有趣的玩具送给他.玩具上有一个数列,数列中某些项的值可能会变化,但同一个时刻最多只有一个值发生变化.现在佳媛姐姐已经研究出了所有变化的可能性,她想请教你,能否选出一个子序列,使得在任意一种变化中,这个子序列都是不降的?请你告诉她这个子序列的最长长度即可.注意:每种变化最多只有一个值发生变化.在样例输入1中,所有的变化是: 1 2 3 2 2 3 1 3 3 1

[cdq分治][树状数组] Bzoj P3262 陌上花开

Description 有n朵花,每朵花有三个属性:花形(s).颜色(c).气味(m),用三个整数表示. 现在要对每朵花评级,一朵花的级别是它拥有的美丽能超过的花的数量. 定义一朵花A比另一朵花B要美丽,当且仅Sa>=Sb,Ca>=Cb,Ma>=Mb. 显然,两朵花可能有同样的属性.需要统计出评出每个等级的花的数量. Input 第一行为N,K (1 <= N <= 100,000, 1 <= K <= 200,000 ), 分别表示花的数量和最大属性值. 以下N

bzoj 1176 [Balkan2007]Mokia - CDQ分治 - 树状数组

Description 维护一个W*W的矩阵,初始值均为S.每次操作可以增加某格子的权值,或询问某子矩阵的总权值.修改操作数M<=160000,询问数Q<=10000,W<=2000000. Input 第一行两个整数,S,W;其中S为矩阵初始值;W为矩阵大小 接下来每行为一下三种输入之一(不包含引号): "1 x y a" "2 x1 y1 x2 y2" "3" 输入1:你需要把(x,y)(第x行第y列)的格子权值增加a 输入

BZOJ1176---[Balkan2007]Mokia (CDQ分治 + 树状数组)

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1176 CDQ第一题,warush了好久.. CDQ分治推荐论文: 1 <从<Cash>谈一类分治算法的应用> 陈丹琦 2 <浅谈数据结构题的几个非经典解法>  许昊然 关于CDQ分治,两种要求:①操作不相互影响  ②可以离线处理 题目描述是有问题的,,初始时 全部为0,不是s 题意:二维平面内,两种操作,1 x y v ,位于(x,y)的值加上v...2 x1,

BZOJ 3262: 陌上花开 cdq分治 树状数组

https://www.lydsy.com/JudgeOnline/problem.php?id=3262 cdq分治板子题,一维排序,一维分治(cdq里的队列),一维数据结构(树状数组). 学dp优化前来复习--以前好像写过这道题但是没写博客啊--在校oj上写的题都没怎么写博客,追悔莫及 1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #

[CDQ分治][树状数组][树套树] Jzoj P3197 K大数查询

Description 有n 个位置和m 个操作.操作有两种,每次操作如果是1 a b c 的形式,表示往第a 个位置到第b 个位置每个位置加入一个数c.如果操作形如2 a b c 的形式,表示询问从第a 个位置到第b 个位置,第c 大的数是多少. Input 在输入文件sequence.in 中,第一行两个数n,m.意义如题目描述.接下来m 行每行形如1 a b c 或者2 a b c 如题目描述. Output 在输出文件sequence.out 中,对于每个询问回答k 大数是多少. Sam