Menci

眉眼如初,岁月如故

在那无法确定的未来
只愿真心如现在一般清澈


「BZOJ 3262」陌上花开 - CDQ

定义一个序列,序列中每个元素都是一个三元组 Ai=(a, b, c) A_i = (a,\ b,\ c)
aiaj, bibj, cicj a_i \leq a_j,\ b_i \leq b_j,\ c_i \leq c_j ,则称 Aj A_j Ai A_i 优。
定义 Ai A_i 的等级为有多少 Aj A_j 满足 Ai A_i Aj A_j 更优。

求每个等级的元素数量。

链接

BZOJ 3262

题解

经典的三维偏序问题,使用 CDQ 分治解决。

首先,将第一维排序,进行 CDQ 分治。分治时保证前一半元素的 a a 始终小于等于后一半,并且两半分别按照 b b 升序。

分治完两半之后,将两半按照 b b 归并,并同时对 c c 维护树状数组,动态查询有多少点的 c c 小于等于当前点。树状数组保证了 c c 的大小关系,归并保证了 b b 的大小关系,排序保证了 a a 的大小关系,CDQ 保证了对一个元素有贡献的所有元素都被考虑的到。

每一次分治完成后需要清空树状数组。

注意可能有多个重复的元素,此时只需记录一个元素出现的次数即可。一个元素会对它本身的答案有贡献,在分治到最后一层时对其赋值即可。

代码

#include <cstdio>
#include <algorithm>

const int MAXN = 100000;
const int MAXK = 200000;

struct Triple {
    int a, b, c, cnt, ans;
} a[MAXN], A[MAXN];

bool operator<(const Triple &a, const Triple &b) {
    return a.a < b.a || (a.a == b.a && a.b < b.b) || (a.a == b.a && a.b == b.b && a.c < b.c);
}

int n, k;

struct BinaryIndexedTree {
    int a[MAXK];

    static int lowbit(const int x) { return x & -x; }

    int query(const int x) {
        int ans = 0;
        for (int i = x; i > 0; i -= lowbit(i)) ans += a[i - 1];
        return ans;
    }

    void update(const int x, const int delta) {
        for (int i = x; i <= k; i += lowbit(i)) a[i - 1] += delta;
    }

    void clean(const int x) {
        for (int i = x; i <= k; i += lowbit(i)) {
            if (a[i - 1]) a[i - 1] = 0;
            else break;
        }
    }
} bit;

inline void cdq(Triple *l, Triple *r) {
    if (l == r) {
        l->ans += l->cnt - 1;
        return;
    }

    Triple *mid = l + (r - l) / 2;

    cdq(l, mid);
    cdq(mid + 1, r);

    static Triple tmp[MAXN];
    for (Triple *p = tmp, *p1 = l, *p2 = mid + 1; p <= tmp + (r - l); p++) {
        if ((p1->b <= p2->b && p1 <= mid) || p2 > r) {
            *p = *p1++;
            bit.update(p->c, p->cnt);
        } else {
            *p = *p2++;
            p->ans += bit.query(p->c);
        }
    }

    for (Triple *p = tmp, *q = l; q <= r; p++, q++) {
        bit.clean(p->c);
        *q = *p;
    }

    /*
    printf("cdq(%ld, %ld): ", l - A + 1, r - A + 1);
    for (Triple *p = l; p <= r; p++) {
        printf("(%d, %d, %d)%s", p->a, p->b, p->c, p == r ? "\n" : ", ");
    }
    */
}

template <typename T>
inline void read(T &x) {
    x = 0;
    register char ch;
    while (ch = getchar(), !(ch >= '0' && ch <= '9'));
    do x = x * 10 + (ch - '0'); while (ch = getchar(), (ch >= '0' && ch <= '9'));
}

template <typename T>
inline void write(T &x) {
    static char s[20];
    int cnt = 0;
    do s[cnt++] = x % 10; while (x /= 10);
    while (cnt--) putchar(s[cnt] + '0');
    putchar('\n');
}

int main() {
    read(n), read(k);
    for (int i = 0; i < n; i++) read(a[i].a), read(a[i].b), read(a[i].c), a[i].cnt = 1;
    // scanf("%d %d", &n, &k);
    // for (int i = 0; i < n; i++) scanf("%d %d %d", &a[i].a, &a[i].b, &a[i].c), a[i].cnt = 1;

    std::sort(a, a + n);

    int cnt = 0;
    for (int i = 0; i < n; i++) {
        if (i == 0 || !(a[i].a == a[i - 1].a && a[i].b == a[i - 1].b && a[i].c == a[i - 1].c)) A[cnt++] = a[i];
        else A[cnt - 1].cnt++;
    }

    cdq(A, A + cnt - 1);

    static int ans[MAXN];
    for (int i = 0; i < cnt; i++) ans[A[i].ans] += A[i].cnt;

    for (int i = 0; i < n; i++) write(ans[i]);
    // for (int i = 0; i < n; i++) printf("%d\n", ans[i]);

    return 0;
}