BZOJ3262 陌上花开 Solution

题意:有n朵花,每朵花有3个属性,若一朵花比另一朵花美丽,当且仅当这朵花的三个属性均不小于另一朵花。一朵花的美丽度等于这朵花比其他多少朵花要美丽。

求美丽度分别为0~n-1的花各有多少朵。


Sol:事实上就是三维偏序。一句话:一维排序,二维CDQ分治,三维树状数组。

How do they work?

首先根据x坐标排序,接着对y,z坐标CDQ分治。

定义Solve(l,r)为处理l~r一段的美丽度。

令mid=(l+r)/2,我们首先进行Solve(l,mid),Solve(mid+1,r).假设我们处理完之后两端的y值均从大到小有序。

我们处理前一段对后一段的影响:按照线性合并的思想,若对后一段进行合并时,则利用树状数组更新后一段的答案。否则更新树状数组。

这样我们能保证Solve(l,r)能正确的更新答案,且使得y一维从小到大有序。另外我们使用树状数组,是为了维护z这一维。

时间复杂度O(nlog^2n).

为了处理相同的影响,我事实上将x和y相同的部分当做一段整体处理。


Code:

#include <cstdio>
#include <cctype>
#include <cstring>
#include <iostream>
#include <algorithm>
 
inline int getc() {
    static const int L = 1 << 15;
    static char buf[L], *S = buf, *T = buf;
    if (S == T) {
        T = (S = buf) + fread(buf, 1, L, stdin);
        if (S == T)
            return EOF;
    }
    return *S++;
}
inline int getint() {
    int c;
    while(!isdigit(c = getc()));
    int tmp = c - '0';
    while(isdigit(c = getc()))
        tmp = (tmp << 1) + (tmp << 3) + c - '0';
    return tmp;
}
 
char buf[600010], *o = buf;
#define _putchar(c) *o++ = c
inline void output(int x) {
    static int stack[10];
    if (!x)
        *o++ = 48;
    else {
        int top = 0;
        for(; x; stack[++top] = x % 10, x /= 10);
        for(int i = top; i >= 1; --i)
            *o++ = 48 + stack[i];
    }
}
 
#define N 100010
struct Case {
    int x, y, z;
    void read() {
        x = getint(), y = getint(), z = getint();
    }
    bool operator < (const Case &B) const {
        return x < B.x || (x == B.x && y < B.y);
    }
}S[N], tmp[N];
int left[N], right[N], cnt, seq[N], _seq[N];
 
#define K 200010
int lim;
int A[K], now[K], tclock;
int ask(int x, int tclock) {
    int res = 0;
    for(; x; x -= x & -x) {
        if (now[x] != tclock) {
            A[x] = 0;
            now[x] = tclock;
        }
        res += A[x];
    }
    return res;
}
void modify(int x, int add, int tclock) {
    for(; x <= lim; x += x & -x) {
        if (now[x] != tclock) {
            A[x] = 0;
            now[x] = tclock;
        }
        A[x] += add;
    }
}
 
int ans[N];
void Divide(int l, int r) {
    register int i, j, k, p;
    if (l == r) {
        ++tclock;
        for(i = left[seq[l]]; i <= right[seq[l]]; ++i)
            modify(S[i].z, 1, tclock);
        for(i = left[seq[l]]; i <= right[seq[l]]; ++i)
            ans[i] += ask(S[i].z, tclock);
        return;
    }
     
    int mid = (l + r) >> 1;
     
    Divide(l, mid);
    Divide(mid + 1, r);
     
    ++tclock;
    i = l, j = mid + 1, k = l;
    while(i <= mid && j <= r) {
        if (S[left[seq[i]]].y <= S[left[seq[j]]].y) {
            _seq[k++] = seq[i];
            for(p = left[seq[i]]; p <= right[seq[i]]; ++p)
                modify(S[p].z, 1, tclock);
            ++i;
        }
        else {
            _seq[k++] = seq[j];
            for(p = left[seq[j]]; p <= right[seq[j]]; ++p)
                ans[p] += ask(S[p].z, tclock);
            ++j;
        }
    }
    while(i <= mid)
        _seq[k++] = seq[i++];
    while(j <= r) {
        for(p = left[seq[j]]; p <= right[seq[j]]; ++p)
            ans[p] += ask(S[p].z, tclock);
        _seq[k++] = seq[j++];
    }
    memcpy(seq + l, _seq + l, sizeof(int) * (r - l + 1));
}
 
int nums[N];
 
int main() {
    //freopen("tt.in", "r", stdin);
    int n = getint();
    lim = getint();
     
    register int i, j;
    for(i = 1; i <= n; ++i)
        S[i].read();
     
    std::sort(S + 1, S + n + 1);
     
    for(i = 1; i <= n; ) {
        for(j = i; S[j].x == S[j + 1].x && S[j].y == S[j + 1].y; ++j);
        left[++cnt] = i, right[cnt] = j;
        i = j + 1;
    }
     
    for(i = 1; i <= cnt; ++i)
        seq[i] = i;
     
    Divide(1, cnt);
     
    for(i = 1; i <= n; ++i)
        ++nums[ans[i]];
     
    for(i = 1; i <= n; ++i) {
        output(nums[i]);
        _putchar('\n');
    }
     
    fwrite(buf, 1, o - buf, stdout);
     
    return 0;
}


你可能感兴趣的:(BZOJ3262 陌上花开 Solution)