[BZOJ2716][天使玩偶angel][CDQ分治]

[BZOJ2716][天使玩偶angel][CDQ分治]

题目大意:

先给出n个点, 然后有m个操作, (1, x, y) 表示查询离(x, y)最近点的曼哈顿距离, (2, x, y) 表示插入点 (x, y)。

CDQ分治相关:

这道题在线的话是可以用KD-Tree维护平面上的点集。但是既然题目没有要求在线那就用CDQ分治做好了。

CDQ分治是一种离线的分治算法。对于一个数据结构问题,无非存在两种操作:修改查询。但是修改和查询往往都是动态的,也就是存在时间维度上的限制,要进行某一个查询,就一定要处理在这个查询之前的所有修改。而CDQ分治可以打破这一维的限制,使得动态查询变为静态查询。用到的方法就是对于一个询问,将它和所有和它有关的修改联系在一起。

(值得注意的是:这一维并不一定是时间上的限制,也有可能是别的。比如BZOJ3262 陌上花开。)

首先我们定义 Divide(L,R) ,表示处理完了区间 [L,R] 中修改对于查询的影响。首先我们引入分治的思想,将区间 [L,R] 分为 [L,mid] [mid+1,R] ,首先处理 Divide(L,mid) ,然后处理 [L,mid] 中修改对于查询的影响,最后处理 Divide(mid+1,R)

可以看出, [mid+1,R] 中的修改并不会影响到 [L,mid] 中的询问,而在 [mid+1,R] 中的询问被 [L,mid] [mid+1,R] (后者会继续分治将范围缩小)所共同影响。假设处理前半区间修改对后半区间查询的复杂度为 O(f(n)) 。那么总时间复杂度为 O(f(n)logn) 。也就是说,CDQ分治用了 O(logn) 的代价打破了时间维度的限制。

思路:

容易看出这道题的修改和查询操作分别为1和2。

我们要把和查询有关的修改整理成某个查询的前缀,在这道题里总共有三个限制 time x y 。其中 (x,y) 因为可以在查询点的任意方位不好搞,需要进行分类讨论,每次只考虑查询左上,右上,左下,右下之中的一个方位的最小曼哈顿距离,然后对于四次求出的答案中取 Min 即可。

考虑到编码方便

  • 右上,左上和右下都可以由原平面水平和竖直翻转得到,只需要编码左下一个方位。

  • 曼哈顿距离的计算方法为: dist((x,y),(x,y))=|xx|+|yy| ,当只考虑左下方位时可以去掉绝对值,原式转化为 dist((x,y),(x,y))=x+y(y+x) 。即在询问 (x,y) 的所有修改前缀 (x,y) 中,我们要求 Max(x+y)

  • 可以用数组数组维护区间最大值

Q:前面说到CDQ分治只能取消 time 一维的限制啊,还有 x y 的限制怎么说?再套两个CDQ吗?

A:哈,不存在的, x 直接排序,树状数组插入下标用 y

坑:

翻转时要保证翻转后的点集 x>0,y>0 ,不然算 lowbit(x) 的时候直接死循环……

代码:

#include 
const int Maxn = 1000010;
const int INF = 1 << 30;
using namespace std;
inline int Max(const int &a, const int &b) {
    return a > b ? a : b;
}
inline int Min(const int &a, const int &b) {
    return a < b ? a : b;
}
inline char get(void) {
    static char buf[1000000], *p1 = buf, *p2 = buf;
    if (p1 == p2) {
        p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin);
        if (p1 == p2) return EOF;
    }
    return *p1++;
}
inline void read(int &x) {
    x = 0; static char c; bool minus = false;
    for (; !(c >= '0' && c <= '9'); c = get()) if (c == '-') minus = true;
    for (; c >= '0' && c <= '9'; x = x * 10 + c - '0', c = get()); if (minus) x = -x;
}
struct Abcd {
    int x, y, k, id;
    friend bool operator <  (const Abcd &a, const Abcd &b) {
        if (a.x == b.x) return a.id < b.id;
        else return a.x < b.x;
    }
} oo[Maxn];
int n, m, Mx, ans[Maxn], t[Maxn];
inline void add(int x, int y) {
    for (x; x <= Mx; x += x & -x) {
        t[x] = Max(t[x], y);
    }
}
inline int query(int x) {
    int sum = 0;
    for (; x; x -= x & -x) {
        sum = Max(sum, t[x]);
    }
    return sum;
}
inline void clear(int x) {
    for (x; x <= Mx; x += x & -x) {
        t[x] = 0;
    }
}
struct Cdq {
    Abcd T[Maxn];
    int n;
    inline void init(const int &n) {
        this->n = n;
        sort(oo + 1, oo + 1 + n);
    }
    inline void solve(int L, int R) {
        if (L >= R) return;
        int mid = (L + R) >> 1, i = L, j = mid + 1;
        for (int k = L; k <= R; k++) {
            if (oo[k].id <= mid) T[i++] = oo[k];
            else T[j++] = oo[k];
        }
        for (int k = L; k <= R; k++) oo[k] = T[k];
        solve(L, mid);
        i = L, j = mid + 1;
        for (; j <= R; j++) if (oo[j].k == 2) {
            for (; i <= mid && oo[i].x <= oo[j].x; i++) if (oo[i].k == 1) {
                add(oo[i].y, oo[i].x + oo[i].y);
            }
            int tmp = query(oo[j].y);
            if (tmp) ans[oo[j].id] = Min(ans[oo[j].id], oo[j].x + oo[j].y - tmp);
        }
        for (j = L; j < i; j++)
            if (oo[j].k == 1) clear(oo[j].y);
        solve(mid + 1, R);
        merge(oo + L, oo + mid + 1, oo + mid + 1, oo + R + 1, T + L);
        for (int i = L; i <= R; i++) oo[i] = T[i];
    }
} cdq;
int main(void) {
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
    read(n), read(m);
    for (int i = 1; i <= n; i++) {
        read(oo[i].x), read(oo[i].y);
        Mx = Max(Mx, Max(++oo[i].x, ++oo[i].y));
        oo[i].id = i;
        oo[i].k = 1;
    }
    for (int i = n + 1; i <= n + m; i++) {
        read(oo[i].k), read(oo[i].x), read(oo[i].y);
        Mx = Max(Mx, Max(++oo[i].x, ++oo[i].y));
        oo[i].id = i;
    }
    for (int i = 1; i <= n + m; i++) ans[i] = INF;
    Mx++;
    cdq.init(n + m);
    cdq.solve(1, n + m);
    for (int i = 1; i <= n + m; i++) oo[i].x = Mx - oo[i].x;
    cdq.init(n + m);
    cdq.solve(1, n + m); 
    for (int i = 1; i <= n + m; i++) oo[i].y = Mx - oo[i].y;
    cdq.init(n + m);
    cdq.solve(1, n + m);
    for (int i = 1; i <= n + m; i++) oo[i].x = Mx - oo[i].x;
    cdq.init(n + m);
    cdq.solve(1, n + m);
    for (int i = 1; i <= n + m; i++)
        if (ans[i] != INF) 
            printf("%d\n", ans[i]);
    return 0;
}

完。

By g1n0st

你可能感兴趣的:(Bzoj,CDQ分治,2017)