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 , m 3 1 0 5 n, m \le 3*10^5 n,m3105
x , y 1 0 6 x,y \le 10^6 x,y106


<mstyle mathcolor="red"> </mstyle> \color{red}{正解部分}

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

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

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

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

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

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

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


, 接下来考虑修改操作, ,

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

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


<mstyle mathcolor="red"> </mstyle> \color{red}{实现部分}

  • 刚开始以为要对每个象限单独处理, 码量极大, 最后发现只需要将整个坐标按 x , y x, y x,y 倒过来即可 .
  • 每次 c d q cdq cdq 分治前需要将肯定不是询问 左上 的点干掉, 否则会 T T T 飞 .
#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\n", Ans[i]);

        return 0;
}
全部评论

相关推荐

10-23 16:33
门头沟学院 Java
本人某中9本科,成绩中等,目前没科研没实习,目前后端学到了javaWeb,开始没定好方向,在学国外课程,走工程路线起步有点晚了,到这个时间点了还在学JavaWeb,顿感迷茫,不知道是坚持走下去还是寒假去准备考研。考研这个路弄得我还是心痒痒的,因为从众考研的人也不在少数,所以会有这方面的心理安慰吧,就是“不行我可以去考研啊”,而且意味着三年的缓冲,为了复试还有积攒经验美化简历,其实现在也可以去申入实验室打杂;就业可能意味着多些工作经验,工程岗应该到后面还是经验大于学历?还是有点迷茫了,求助好心人有无路线启发
千千倩倩:同27给点建议,现在这个时间点可以快速看完外卖和点评,不用跟着敲,但一定要在看的时候总结每个部分的整个业务流程,对其中的实现有一个大概的印象。然后直接开始看八股,刷算法。八股和算法最好还是在项目学习中穿插着看。如果计算机基础,算法这些基础好,加上每天刻苦学习,两周可以达到勉强能面试的水平,到时候就直接海投中小厂,在约面和面试的过程中不断巩固知识。没找到实习也没关系,就当积累经验。再沉淀一波直接明年三月开始投暑期,毕竟是9本,总是有面试机会的,只要你这三个月不懈怠,面试发挥得一定不错,只要拿到一个中,大厂暑期实习,秋招就有竞争力了。总得而言,现在还有机会,但是时间非常紧张,需要你结合自己情况考虑,共勉
你会选择考研还是直接就业
点赞 评论 收藏
分享
LXXXXd:有点杂,想搞自动化的话没必要把法律的经历写上去
点赞 评论 收藏
分享
09-24 18:30
已编辑
长春工业大学 产品经理
小肥罗:HR就是好人的缩写哈哈哈哈
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务