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;
}

转载于:https://www.cnblogs.com/herald/p/10065997.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值