zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    第二章:Java内存区域
    第六章:内核数据结构
    第十章:内核同步方法
    第九章:内核同步介绍
    第九章:zookeeper内部原理
    第三章:开始使用zookeeper的API
    第二章:了解zookeeper
    【观点】健康的心智是中国未来最大的生产力
    【流程管理分享】人事不分,问题不断
    为什么中国人勤劳而不富裕?为什么宋朝富裕而不强大?
  • 原文地址:https://www.cnblogs.com/zbr162/p/11822495.html
Copyright © 2011-2022 走看看