P4169 [Violet]天使玩偶/SJY摆棋子 [CDQ分治]

使天使玩偶

Ayu 在七年前曾经收到过一个天使玩偶,当时她把它当作时间囊埋在了地下。而七年后 的今天,Ayu 却忘了她把天使玩偶埋在了哪里,所以她决定仅凭一点模糊的记忆来寻找它。

我们把 Ayu 生活的小镇看作一个二维平面坐标系,而 Ayu 会不定时地记起可能在某个点 (x, y) 埋下了天使玩偶;或者 Ayu 会询问你,假如她在 (x,y) ,那么她离近的天使玩偶可能埋下的地方有多远。

因为 Ayu 只会沿着平行坐标轴的方向来行动,所以在这个问题里我们定义两个点之间的距离为dist(A,B)=|Ax-Bx|+|Ay-By|。其中 Ax 表示点 A的横坐标,其余类似。

输入格式

第一行包含两个整数n和m ,在刚开始时,Ayu 已经知道有n个点可能埋着天使玩偶, 接下来 Ayu 要进行m 次操作

接下来n行,每行两个非负整数 (xi,yi),表示初始n个点的坐标。

再接下来m 行,每行三个非负整数 t,xi,yi。

如果t=1 ,则表示 Ayu 又回忆起了一个可能埋着玩偶的点 (xi,yi) 。

如果t=2 ,则表示 Ayu 询问如果她在点 (xi,yi) ,那么在已经回忆出来的点里,离她近的那个点有多远

n,m3105n, m le 3*10^5
x,y106x,y le 10^6


color{red}{正解部分}

先考虑没有修改操作的情况 downarrow

给出若干点对 (x,y)(x, y), 和若干询问 (xp,yp)(x_p, y_p), 询问距离 (xp,yp)(x_p, y_p) 最近的点离它的距离 .

即要求的是 mini=1n(xix+yiy)minlimits_{i=1}^n(|x_i-x|+|y_i-y|), 这个绝对值符号不好处理, 考虑分类讨论,

对于一个点 (x,y)(x, y), 距离它较近的点只可能在它的四个方向: 左上, 右上, 左下, 右下,
逐一考虑怎么解决, 先看 左上 的情况,
化简绝对值符号得到 d=xxi+yyi=x+y(xi+yi)d = x-x_i+y-y_i = x+y - (x_i+y_i),
为了使得 dd 更小, 需要使得 xi+yix_i+y_i 尽量大,
于是可以想到先把 询问xix_i 排序, 然后从左向右扫,

  • 遇到 点 (xi,yi)(x_i, y_i), 在以 yiy_i 为下标的 线段树/树状数组 中更新 xi+yix_i+y_i 的最大值 .
  • 遇到 询问 (x,y)(x, y), 查询 [1,y][1, y] 的区间最大值即可 .

到这里已经解决了 左上 的问题, 其他三个方向同理 .

上方其实是一个二维偏序的问题, 排序 维护 xx 的偏序, 线段树/树状数组 维护 yy 的偏序 .


,接下来考虑修改操作,

修改的操作无非就是加了一维时间偏序, 整个变为三位偏序, 没打过模板的可以看 这里 .

第一维时间排序维护, 第二维 xx 归并维护, 第三维 yy 树状数组 维护 .


color{red}{实现部分}

  • 刚开始以为要对每个象限单独处理, 码量极大, 最后发现只需要将整个坐标按 x,yx, y 倒过来即可 .
  • 每次 cdqcdq 分治前需要将肯定不是询问 左上 的点干掉, 否则会 TT 飞 .
#include<bits/stdc++.h>
#define reg register

const int maxn = 1e6 + 5;
const int inf = 0x3f3f3f3f;

int N;
int M;
int tot;
int Lim;
int Max_x;
int Max_y;
int max_x;
int max_y;
int new_tot;
int node_cnt;
int Ans[maxn];

int read(){
        char c;
        int s = 0, flag = 1;
        while((c=getchar()) && !isdigit(c))
                if(c == '-'){ flag =-1, c = getchar(); break ; }
        while(isdigit(c)) s = s*10 + c-'0', c = getchar();
        return s * flag;
}

struct Node{ int x, y, opt, tim; } A[maxn], B[maxn], tmp[maxn];

struct Bit_t{
        int v[maxn];
        inline void clear(int k){ while(k <= max_y) v[k] = 0, k += k&-k; }
        inline void update(int k, int x){ while(k<=max_y)v[k]=std::max(v[k],x),k+=k&-k; }
        inline int Query(int k){ int s=0; while(k)s=std::max(s,v[k]),k-=k&-k; return s; }
} bit_t;

void Del(){
        max_x = 0, max_y = 0; new_tot = 0;
        for(reg int i = 1; i <= tot; i ++)
                if(A[i].opt) max_x = std::max(max_x, A[i].x), max_y = std::max(max_y, A[i].y);
        for(reg int i = 1; i <= tot; i ++)
                if(A[i].x <= max_x && A[i].y <= max_y) tmp[++ new_tot] = A[i];
        for(reg int i = 1; i <= new_tot; i ++) A[i] = tmp[i];
}

inline void merge_sort(int l, int r){
        if(l >= r) return ;
        int mid = l+r >> 1;
        merge_sort(l, mid), merge_sort(mid+1, r);

        int t1 = l, t2 = mid+1, t3 = l;
        while(t1 <= mid && t2 <= r){
                if(A[t1].x <= A[t2].x){
                        if(!A[t1].opt) bit_t.update(A[t1].y, A[t1].x+A[t1].y);
                        tmp[t3 ++] = A[t1 ++];
                }else{
                        int res = bit_t.Query(A[t2].y);
                        if(res && A[t2].opt) Ans[A[t2].tim] = std::min(Ans[A[t2].tim], A[t2].x+A[t2].y-res);
                        tmp[t3 ++] = A[t2 ++];
                }
        }
        while(t2 <= r){ 
                int res = bit_t.Query(A[t2].y);
                if(res && A[t2].opt) Ans[A[t2].tim] = std::min(Ans[A[t2].tim], A[t2].x+A[t2].y-res); 
                tmp[t3 ++] = A[t2 ++];
        }
        for(reg int i = l; i < t1; i ++) bit_t.clear(A[i].y);
        while(t1 <= mid) tmp[t3 ++] = A[t1 ++];
        for(reg int i = l; i <= r; i ++) A[i] = tmp[i];
}

int main(){
        N = read(), M = read();
        for(reg int i = 1; i <= N; i ++) A[i].x = read(), A[i].y = read(), A[i].tim = i;
        tot = N;
        for(reg int i = 1; i <= M; i ++){
                tot ++;
                int t = read();
                A[tot].x = read(), A[tot].y = read();
                A[tot].tim = tot, A[tot].opt = (t == 2);
        }
        for(reg int i = 1; i <= tot; i ++){
                A[i].x ++, A[i].y ++, B[i] = A[i];
                Max_x = std::max(Max_x, A[i].x);
                Max_y = std::max(Max_y, A[i].y);
                Ans[i] = inf;
        }
        Del(); merge_sort(1, new_tot);

        for(reg int i = 1; i <= tot; i ++) A[i] = B[i], A[i].x = Max_x - A[i].x + 1;
        Del(); merge_sort(1, new_tot);

        for(reg int i = 1; i <= tot; i ++) A[i] = B[i], A[i].y = Max_y - A[i].y + 1;
        Del(); merge_sort(1, new_tot);

        for(reg int i = 1; i <= tot; i ++) A[i] = B[i], A[i].x = Max_x - A[i].x + 1, A[i].y = Max_y - A[i].y + 1;
        Del(); merge_sort(1, new_tot);

        for(reg int i = N+1; i <= tot; i ++) if(B[i].opt) printf("%d
", Ans[i]);

        return 0;
}
原文地址:https://www.cnblogs.com/zbr162/p/11822495.html