合辑:CDQ分治

听考拉的主人说CDQ分治是她考NOI的时候现yy出来的,太强了Orz

CDQ分治主要是一种思想,用来处理离线的问题。

学长说可以优化DP,但我觉得适用面还蛮窄的,主要还是处理序列问题,最主要的应该就是偏序。

CDQ分治的主要思想就是用前一个子问题解决后一个子问题,比如你把一个询问区间二分,左边的区间维护答案,右边的区间查询左边区间维护的答案。至于右边的维护操作和左边的操作?交给下一层去解决了。

这就限制了CDQ分治的适用范围,需要修改操作对询问的贡献是独立的且互不影响。

Problem A:陌上花开

维护三维偏序。

树套树裸题,切了,走了(误

树套树又臭又长能不写就不写,CDQ好写好调,来写CDQ

先想二维偏序我们是怎么解决的:先按照第一维排个序,在第二维统计答案

三维偏序多了一维,还是在最外层第三维统计答案,第二维套个CDQ。

具体一点,先按a排个序,顺便去重。然后跑CDQ,通过跑归并把b排序,同时用数据结构维护c的偏序,这里是树状数组,同时之前的归并保证了b的有序。

#include <bits/stdc++.h>

const int N = 200000 + 233;
int n, k, ans[N];
struct Flower {int a, b, c, cnt, f;} a[N], b[N];

bool cmp(const Flower &x, const Flower &y) {
    return (x.a == y.a && x.b == y.b) ? x.c < y.c : (x.a == y.a) ? x.b < y.b : x.a < y.a;
}

struct BIT {
    int t[N];
    BIT() {memset(t, 0, sizeof(t));}
    inline void Add(int x, int v) {
        while (x <= k) t[x] += v, x += x & -x;
    }
    inline int Ask(int x) {
        int ret = 0;
        while (x) ret += t[x], x -= x & -x;
        return ret;
    }
} bit;

void CDQ(int l, int r) {
    if (l == r) return;
    int mid = (l + r) >> 1, pl = l, pr = mid + 1, p = l;
    CDQ(l, mid), CDQ(mid + 1, r);
    while (pl <= mid || pr <= r) {
        if ((a[pl].b <= a[pr].b && pl <= mid) || (pr > r)) bit.Add(a[pl].c, a[pl].cnt), b[p++] = a[pl++];
        else a[pr].f += bit.Ask(a[pr].c), b[p++] = a[pr++];
    }
    for (int i = l; i <= mid; i++) bit.Add(a[i].c, -a[i].cnt);
    for (int i = l; i <= r; i++) a[i] = b[i];
}

signed main() {
    scanf("%d%d", &n, &k);
    for (int i = 1; i <= n; i++)
        scanf("%d%d%d", &a[i].a, &a[i].b, &a[i].c), a[i].cnt = 1;
    std::sort(a + 1, a + 1 + n, cmp);
    int p = 1;
    for (int i = 2; i <= n; i++) {
        if (a[i].a == a[p].a && a[i].b == a[p].b && a[i].c == a[p].c) a[p].cnt++;
        else a[++p] = a[i];
    }
    CDQ(1, p);
    for (int i = 1; i <= p; i++) ans[a[i].f + a[i].cnt - 1] += a[i].cnt;
    for (int i = 0; i < n; i++) printf("%d\n", ans[i]);
    return 0;
}

Problem B & C: Mokia & 简单题

双倍的快乐

查询矩阵权值容易想到二维前缀和,把右上角和左下角的加上,把左上角和右下角的减掉。查询操作就被拆成了4个。

关于修改操作,一个修改对查询有贡献,当且仅当它的xyt都不大于查询,这就又成了三维偏序,继续CDQ。

老生长谈的问题,y1之类的变量名是会撞的,你必CE。解决方法两个,一个是你换变量名,或者如果你像我一样固执,可以开个namespace规避打击。

简单题在Luogu是强制在线的,双倍经验请谨慎,你CDQ必死(

#include <bits/stdc++.h>

namespace Gekoo {
    const int N = 2000000 + 233;
    int s, w, qwq, qaq, ans[N];

    struct Command {
        int opt, x, y, a, p;
    } cmd[N], tmp[N];

    struct Bit {
        int t[N];
        inline void Add(int p, int d) {
            for (; p <= w; p += p & -p) t[p] += d;
        }
        inline int Ask(int p) {
            int ret = 0;
            for (; p; p -= p & -p) ret += t[p];
            return ret;
        } 
    } BIT;

    inline int R() {
        int a = 0; char c = getchar();
        while (!isdigit(c)) c = getchar();
        while (isdigit(c)) a = a * 10 + c - '0', c = getchar();
        return a;
    }

    void CDQ(int l, int r) {
        if (l == r) return;
        int mid = (l + r) >> 1, pl = l, pr = mid + 1, p = l;
        CDQ(l, mid), CDQ(mid + 1, r);
        while (pl <= mid || pr <= r) {
            if ((cmd[pl].x <= cmd[pr].x && pl <= mid) || pr > r) {
                if (cmd[pl].opt == 1) BIT.Add(cmd[pl].y, cmd[pl].a);
                tmp[p++] = cmd[pl++];
            } else {
                if (cmd[pr].opt == 2) {
                    if (cmd[pr].p > 0) ans[cmd[pr].p] += BIT.Ask(cmd[pr].y);
                    else ans[-cmd[pr].p] -= BIT.Ask(cmd[pr].y);
                }
                tmp[p++] = cmd[pr++];
            }
        }
        for (int i = l; i <= mid; i++) 
            if (cmd[i].opt == 1) BIT.Add(cmd[i].y, -cmd[i].a);
        for (int i = l; i <= r; i++) cmd[i] = tmp[i];
    }

    signed QAQ() {
        s = R(), w = R();
        while (19260817 < 20030325) {
            int opt = R();
            if (opt == 3) break;
            if (opt == 1) cmd[++qwq].opt = 1, cmd[qwq].x = R(), cmd[qwq].y = R(), cmd[qwq].a = R();
            if (opt == 2) {
                int x1 = R(), y1 = R(), x2 = R(), y2 = R();
                ans[++qaq] = (x2 - x1 + 1) * (y2 - y1 + 1) * s;
                cmd[++qwq].opt = 2, cmd[qwq].p = qaq, cmd[qwq].x = x1 - 1, cmd[qwq].y = y1 - 1;
                cmd[++qwq].opt = 2, cmd[qwq].p = qaq, cmd[qwq].x = x2, cmd[qwq].y = y2;
                cmd[++qwq].opt = 2, cmd[qwq].p = -qaq, cmd[qwq].x = x1 - 1, cmd[qwq].y = y2;
                cmd[++qwq].opt = 2, cmd[qwq].p = -qaq, cmd[qwq].x = x2, cmd[qwq].y = y1 - 1;
            }
        }
        CDQ(1, qwq);
        for (int i = 1; i <= qaq; i++)
            printf("%d\n", ans[i]);
        return 0;
    }
}

signed main() {
    return Gekoo::QAQ();
}

Problem C: 天使玩偶

绝对值维护不能,

#include <bits/stdc++.h>

const int N = 1000000 + 233, INF = 0x3f3f3f3f;
int n, m, ans[N], real_ans[N], mx_x, mx_y;
struct Doll {
    int x, y, t, id;
    friend bool operator <(Doll x, Doll y) {
        return x.id < y.id;
    }
} d[N], tmp[N];

struct Bit {
    int t[N];
    inline void Add(int x, int d) {
        for (; x <= mx_y; x += x & -x) t[x] = std::max(t[x], d);
    }
    inline int Ask(int x) {
        int ret = -INF;
        for (; x; x -= x & -x) ret = std::max(ret, t[x]);
        return ret;
    }
    inline void Clear(int x) {
        for (; x <= mx_y; x += x & -x) t[x] = -INF;
    }
} BIT;

inline int R() {
    int a = 0; char c = getchar();
    while (!isdigit(c)) c = getchar();
    while (isdigit(c)) a = a * 10 + c - '0', c = getchar();
    return a;
}

void CDQ(int l, int r) {
    if (l == r) return;
    int mid = (l + r) >> 1, pl = l, pr = mid + 1, p = l;
    CDQ(l, mid), CDQ(mid + 1, r);
    while (pl <= mid || pr <= r) {
        if ((pl <= mid && d[pl].x <= d[pr].x) || pr > r) {
            if (d[pl].t == 1) BIT.Add(d[pl].y, d[pl].x + d[pl].y);
            tmp[p++] = d[pl++];
        }
        else {
            if (d[pr].t == 2) ans[d[pr].id] = std::max(ans[d[pr].id], BIT.Ask(d[pr].y));
            tmp[p++] = d[pr++];
        }
    }
    for (int i = l; i <= mid; i++) BIT.Clear(d[i].y);
    for (int i = l; i <= r; i++) d[i] = tmp[i];
}

signed main() {
    n = R(), m = R();
    for (int i = 1; i <= n; i++)
        d[i].x = R() + 1, d[i].y = R() + 1, d[i].t = 1, d[i].id = i, mx_x = std::max(d[i].x, mx_x), mx_y = std::max(d[i].y, mx_y);
    for (int i = 1; i <= m; i++)
        d[i + n].t = R(), d[i + n].id = i + n, mx_x = std::max(d[i + n].x = R() + 1, mx_x), mx_y = std::max(d[i + n].y = R() + 1, mx_y);
    for (int i = 1; i <= n + m; i++)
        real_ans[i] = INF, ans[i] = -INF;
    for (int i = 0; i <= mx_x + mx_y + 2; i++) BIT.t[i] = -INF;
    mx_x++, mx_y++, CDQ(1, n + m);

    std::sort(d + 1, d + 1 + n + m);
    for (int i = 1; i <= n + m; i++)
        real_ans[i] = std::min(real_ans[i], d[i].x + d[i].y - ans[i]), d[i].x = mx_x - d[i].x, ans[i] = -INF;
    CDQ(1, n + m);

    std::sort(d + 1, d + 1 + n + m);
    for (int i = 1; i <= n + m; i++)
        real_ans[i] = std::min(real_ans[i], d[i].x + d[i].y - ans[i]), d[i].y = mx_y - d[i].y, ans[i] = -INF;
    CDQ(1, n + m);

    std::sort(d + 1, d + 1 + n + m);
    for (int i = 1; i <= n + m; i++)
        real_ans[i] = std::min(real_ans[i], d[i].x + d[i].y - ans[i]), d[i].x = mx_x - d[i].x, ans[i] = -INF; 
    CDQ(1, n + m);

    std::sort(d + 1, d + 1 + n + m);
    for (int i = 1; i <= n + m; i++)
        real_ans[i] = std::min(real_ans[i], d[i].x + d[i].y - ans[i]);
    for (int i = 1; i <= n + m; i++)
        if (d[i].t == 2)
            printf("%d\n", real_ans[i]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/gekoo/p/11249445.html
今日推荐