CDQ分治
一、关于CDQ分治
现在给你很多操作,每次给出\(pos\)以及\(x\),表示\(pos\)位置上的数加上x,然后给出很多询问,每次问\(l\)到\(r\)之间的和。
显然这个东西\(O(n)\)是可以解决的。
但是如果操作和询问交叉的话(不用树状数组),就不能用这种方法了。但是可以发现如果一个点在\(i\)的时刻被加了一个数,那么在\(i\)及以后的时刻这个位置都会加上这个数(因为不会存在删除的操作)。
所以可以尝试利用这种不删除的特点。我们考虑一个做法。假设要处理\([l,r]\)之间的操作。我们先处理\([l,\lfloor (l+r)/2 \rfloor]\)之间的操作,再处理\([\lfloor (l+r)/2 \rfloor+1,r]\)之间的操作,再考虑左边对右边的影响。
那这道就可以变成刚开始的情况了。
二、题目
1.Luogu P4390 [BOI2007]Mokia 摩基亚
题意:
有n次操作,每次操作要么给一个二维坐标的点加上一个数,要么询问某个矩形内的数的和。考虑CDQ和差分。
题解:
在合并左右的影响时,用差分的思想。如果求的是\((x1,y1)\)到\((x2,y2)\)的和。就是求横坐标在\(1\)至\(x1-1\)间纵坐标在\(y1\)至\(y2\)的树的和,还横坐标在\(1\)至\(x2\)间纵坐标在\(y1\)至\(y2\)的树的和。考虑扫描线,把询问拆成两个询问,处理每个询问前把所有横坐标在当前询问之前的点都加入当中,用树状数组或线段数维护即可。
代码:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 200000;
const int MAXX = 2000000;
struct Point {
int x, y, w;
} b[MAXN + 5];
struct Query {
int id, flag, x, sy, ey;
} c[MAXN + 5 << 1];
int ans[MAXN + 5], a[MAXN + 5][5], opt, n;
bool cmpb(Point a, Point b) { return a.x < b.x; }
bool cmpc(Query a, Query b) { return a.x < b.x; }
class BinaryIndexTree {
private:
int c[MAXX + 5];
void _add(int x, int y) {
for (int i = x; i <= MAXX; i += i & -i) c[i] += y;
}
void _dec(int x, int y) {
for (int i = x; i <= MAXX; i += i & -i) c[i] -= y;
}
int _query(int x) {
int ret = 0;
for (int i = x; i; i -= i & -i) ret += c[i];
return ret;
}
public:
void add(int x, int y) { _add(x, y); }
int query(int x, int y) { return _query(y) - _query(x - 1); }
void dec(int x, int y) { _dec(x, y); }
} bit;
void cdq(int l, int r) {
if (l == r) return;
int mid = l + r >> 1;
cdq(l, mid);
cdq(mid + 1, r);
int bcnt = 0, ccnt = 0;
for (int i = l; i <= mid; ++i)
if (a[i][0] == 1)
b[++bcnt] = (Point){a[i][1], a[i][2], a[i][3]};
for (int i = mid + 1; i <= r; ++i)
if (a[i][0] == 2) {
c[++ccnt] = (Query){i, -1, a[i][1] - 1, a[i][2], a[i][4]};
c[++ccnt] = (Query){i, 1, a[i][3], a[i][2], a[i][4]};
}
sort(b + 1, b + bcnt + 1, cmpb);
sort(c + 1, c + ccnt + 1, cmpc);
int bptr = 0;
for (int i = 1; i <= ccnt; ++i) {
while (bptr < bcnt && b[bptr + 1].x <= c[i].x) {
bit.add(b[bptr + 1].y, b[bptr + 1].w);
++bptr;
}
ans[c[i].id] += c[i].flag * bit.query(c[i].sy, c[i].ey);
}
while (bptr--) bit.dec(b[bptr + 1].y, b[bptr + 1].w);
}
int main() {
scanf("%d", &opt);
while (opt != 3) {
if (opt == 1) {
a[++n][0] = opt;
scanf("%d%d%d", &a[n][1], &a[n][2], &a[n][3]);
} else if (opt == 2) {
a[++n][0] = opt;
scanf("%d%d%d%d", &a[n][1], &a[n][2], &a[n][3], &a[n][4]);
}
scanf("%d", &opt);
}
cdq(1, n);
for (int i = 1; i <= n; ++i)
if (a[i][0] == 2)
printf("%d\n", ans[i]);
return 0;
}
2.Luogu P3810 【模板】三维偏序(陌上花开)
题意:
题目很简洁
题解:
这道题目和上一题其实差不多,我们考虑将一维排序,然后对于每个数只找前面的满足条件的。问题是第一维相同的可能统计不到,所以后统计答案的时候对于每个相同的三元组,答案为最大的那个的值。
代码:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <map>
#include <algorithm>
using namespace std;
typedef pair <int, int> PII;
const int MAXN = 100000;
struct Node {
int x, y, z;
bool operator <(Node rhs) const {
return x < rhs.x || x == rhs.x && y < rhs.y || x == rhs.x && y == rhs.y && z < rhs.z;
}
};
struct Flower {
int s, c, m;
} a[MAXN + 5];
struct Point {
int c, m;
} b[MAXN + 5];
struct Query {
int id, c, m;
} c[MAXN + 5];
int n, k, cnt[MAXN + 5], ans[MAXN + 5];
map <Node, int> mapp;
bool pntcmp(Point a, Point b) {
return a.c < b.c;
}
bool qrycmp(Query a, Query b) {
return a.c < b.c;
}
bool flwcmp(Flower a, Flower b) {
return a.s < b.s || a.s == b.s && a.c < b.c || a.s == b.s && a.c == b.c && a.m < b.m;
}
class BinaryIndexTree {
private:
static const int MAXX = 200000;
int c[MAXX + 5];
void _add(int x) {
for (int i = x; i <= MAXX; i += i & -i) ++c[i];
}
void _dec(int x) {
for (int i = x; i <= MAXX; i += i & -i) --c[i];
}
int _query(int x) {
int ret = 0;
for (int i = x; i; i -= i & -i) ret += c[i];
return ret;
}
public:
void add(int x) { _add(x); }
int query(int x, int y) { return _query(y) - _query(x - 1); }
void dec(int x) { _dec(x); }
} bit;
void cdq(int l, int r) {
if (l == r) return;
int mid = l + r >> 1;
cdq(l, mid);
cdq(mid + 1, r);
int bcnt = 0, ccnt = 0;
for (int i = l; i <= mid; ++i)
b[++bcnt] = (Point){a[i].c, a[i].m};
for (int i = mid + 1; i <= r; ++i)
c[++ccnt] = (Query){i, a[i].c, a[i].m};
sort(b + 1, b + bcnt + 1, pntcmp);
sort(c + 1, c + ccnt + 1, qrycmp);
int bptr = 0;
for (int i = 1; i <= ccnt; ++i) {
while (bptr < bcnt && b[bptr + 1].c <= c[i].c) {
bit.add(b[bptr + 1].m);
++bptr;
}
ans[c[i].id] += bit.query(1, c[i].m);
}
while (bptr--) bit.dec(b[bptr + 1].m);
}
int main() {
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; ++i)
scanf("%d%d%d", &a[i].s, &a[i].c, &a[i].m);
sort(a + 1, a + n + 1, flwcmp);
cdq(1, n);
for (int i = 1; i <= n; ++i)
mapp[(Node){a[i].s, a[i].c, a[i].m}] = max(mapp[(Node){a[i].s, a[i].c, a[i].m}],
ans[i]);
for (int i = 1; i <= n; ++i)
++cnt[mapp[(Node){a[i].s, a[i].c, a[i].m}]];
for (int i = 0; i < n; ++i)
printf("%d\n", cnt[i]);
return 0;
}