• [算法模版]曼哈顿距离最小生成树


    [算法模版]曼哈顿距离最小生成树

    算法描述

    先说结论。做法就是把平面等分成8块。可以证明以(i)为坐标原点的八个区域中,每个区域都只有离(i)最近的点可能和(i)连边。一旦我们确定了这些点,我们就把边数压缩到了(8n),就可以直接跑了。

    对于在以(i)为坐标原点(y)轴正半轴右边那个区域(右上角),区域内离他最近的点(j)满足(y_j-x_jgeq y_i-x_i),同时保证(y_j+x_j)最小。

    第一个限制保证了在右上角那个区间,第二个保证了距离最近。

    我们就可以用数据结构来维护这个东西。(把(y-x)离散一下,维护([y-x,infty])(y+x)最小的是多少就好了)

    对于其他区域最近点的求解,我们可以把它对称到右上角的区域来解决。

    证明

    下面我们将证明

    (i)为坐标原点的八个区域中,每个区域都只有离(i)最近的点可能和(i)连边

    曼哈顿距离最小生成树

    例题

    Dragonstone

    这道题还要用到一个结论:要令两个点之间路径上最大边最小,选择的路径就是最小生成树上路径。(因为如果最大的路径不是最小生成树上的路径一定不优)

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<map>
    #define pr pair<int,int>
    #define ll long long
    using namespace std;
    const int maxn=200500;
    #define inf 1061109567
    const int fff=(int)2e9+1000;
    int n,val[maxn],who[maxn],bk[maxn][2],mem[maxn];
    int changex[10]={0,1,1,-1,-1,1,1,-1,-1};
    int changey[10]={0,1,-1,-1,1,1,-1,-1,1};
    int changec[10]={0,0,0,0,0,1,1,1,1};
    struct gg {
        int x,y,dif,sum,ox,oy,rk,id;//rk代表离散化之后的排名,id为最初输入的编号
    }node[maxn];
    struct fuck {
      int fi,se;
    };
    namespace seg {
        int mi[maxn<<2],num[maxn<<2];
        void push_up(int root) {
            mi[root]=min(mi[root<<1],mi[root<<1|1]);
            if(mi[root<<1]>mi[root<<1|1]){num[root]=num[root<<1|1];}
            else{num[root]=num[root<<1];}
        }
        void build(int l,int r,int root) {
            if(l==r){mi[root]=node[l].sum;num[root]=l;return;}
            int mid=(l+r)>>1;
            build(l,mid,root<<1);build(mid+1,r,root<<1|1);
            push_up(root);
        }
        void update(int l,int r,int root,int tar) {
            if(l==r){mi[root]=mem[who[l]];num[root]=who[l];return;}
            int mid=(l+r)>>1;
            if(tar<=mid)update(l,mid,root<<1,tar);
            else update(mid+1,r,root<<1|1,tar);
            push_up(root);
        }
        fuck qnum(int l,int r,int root,int tl,int tr) {
            fuck re={fff,0},tmp;
            if(tl>tr)return re;
            if(tl<=l&&r<=tr){fuck rea={mi[root],num[root]};return rea;}
            int mid=(l+r)>>1;
            if(tl<=mid){tmp=qnum(l,mid,root<<1,tl,tr);if(tmp.fi<re.fi)re=tmp;}
            if(tr>=mid+1){tmp=qnum(mid+1,r,root<<1|1,tl,tr);if(tmp.fi<re.fi)re=tmp;}
            return re;
        }
    }
    namespace tree {
        int cnt,head[maxn],fa[maxn],tot,f[maxn][19],len[maxn][19],dep[maxn];//tot为边数
        map<pair<int,int>,bool>ex;
        struct edge {
            int u,v,w,next;
        }side[maxn*2*8];
        struct data {
            int u,v,w;
        }con[maxn*8];
        void insert(int u,int v,int w) {
            struct edge add={u,v,w,head[u]};side[++cnt]=add;head[u]=cnt;
        };
        int get(int x){
            if(fa[x]==x)return x;
            fa[x]=get(fa[x]);
            return fa[x];
        }
        void uni(int x,int y) {
            fa[get(x)]=get(y);
        }
        bool cop2(data x,data y){return x.w<y.w;}
        void dfs(int now,int g,int w) {
            f[now][0]=g;len[now][0]=w;dep[now]=dep[f[now][0]]+1;
            for(int i=1;i<=18;i++){f[now][i]=f[f[now][i-1]][i-1];len[now][i]=max(len[now][i-1],len[f[now][i-1]][i-1]);}
            for(int i=head[now];i;i=side[i].next) {
                int v=side[i].v;if(v==f[now][0]||f[v][0]==now)continue;dfs(v,now,side[i].w);
            }
        }
        void span(){
            sort(con+1,con+1+tot,cop2);
            for(int i=1;i<=n;i++)fa[i]=i;
            for(int i=1;i<=tot;i++) {
                int u=con[i].u,v=con[i].v,w=con[i].w;
                if(get(u)!=get(v)){
                    uni(u,v);
                    insert(u,v,w);insert(v,u,w);
                }
            }
            dfs(1,0,0);
        }
        int lca(int u,int v) {
            int ma=0;
            if(dep[u]<dep[v])swap(u,v);
            for(int i=18;i>=0;i--)if(dep[f[u][i]]>=dep[v]){ma=max(ma,len[u][i]);u=f[u][i];}
            if(u==v)return ma;
            for(int i=18;i>=0;i--) {
                if(f[u][i]!=f[v][i]) {
                    ma=max(ma,len[u][i]);ma=max(ma,len[v][i]);
                    u=f[u][i],v=f[v][i];
                }
            }
            return max(ma,max(len[u][0],len[v][0]));
        }
    }
    inline int get_dis(int x,int y) {
        return abs(bk[x][0]-bk[y][0])+abs(bk[x][1]-bk[y][1]);
    }
    inline bool cop1(gg x,gg y) {
        return x.dif<y.dif;
    }
    inline bool cop3(gg x,gg y) {
        if(x.x==y.x) {
            return x.y<y.y;
        }
        return x.x<y.x;
    }
    inline bool cop4(gg x,gg y) {
        return x.id<y.id;
    }
    void init() {
        memset(tree::head,0,sizeof(tree::head));
        //  xdmemset(seg::mi,0x3f,sizeof(seg::mi));
        tree::ex.clear();tree::tot=tree::cnt=0;
        memset(tree::f,0,sizeof(tree::f));memset(tree::len,0,sizeof(tree::len));memset(tree::dep,0,sizeof(tree::dep));
    }
    void solve() {
        scanf("%d",&n);
        for(int i=1;i<=n;i++) {
            scanf("%d%d",&node[i].ox,&node[i].oy);node[i].id=i;
    
        }
        init();
        for(int i=1;i<=8;i++) {//枚举4种变换
            memset(seg::mi,0x3f,sizeof(seg::mi));
            //memset(seg::num,0,sizeof(seg::num));
            //sort(node+1,node+1+n,cop4);
            for(int j=1;j<=n;j++) {
                node[j].x = node[j].ox * changex[i];
                node[j].y = node[j].oy * changey[i];
                if(changec[i])swap(node[j].x,node[j].y);
                bk[node[j].id][0]=node[j].x,bk[node[j].id][1]=node[j].y;
                node[j].dif = node[j].y - node[j].x;
                node[j].sum = node[j].x + node[j].y;mem[node[j].id]=node[j].sum;
            }
            sort(node+1,node+1+n,cop1);//val:dif第j名dif是多少,who:dif第j名id是多少
            for(int j=1;j<=n;j++){node[j].rk=j;val[j]=node[j].dif;who[j]=node[j].id;}
            // seg::build(1,n,1);
            sort(node+1,node+1+n,cop3);
            for(int j=n;j>=1;j--) {//枚举哪个点为坐标原点
                seg::update(1,n,1,node[j].rk);
                int l=lower_bound(val+1,val+1+n,node[j].dif)-val;
                fuck point1=seg::qnum(1,n,1,l,node[j].rk-1);//查询到一个最小值,返回的second是那个点的编号
                fuck point2=seg::qnum(1,n,1,node[j].rk+1,n);
                fuck point;
                if(point1.fi<point2.fi)point=point1;
                else point=point2;
                int u=point.se,v=node[j].id;if(!u||!v||point.fi==inf)continue;
                if(u>v)swap(u,v);
                if(u==v)continue;
                //tree::ex[make_pair(u,v)]=1;
                //cerr<<u<<' '<<v<<endl;
                //tree::insert(u,v,get_dis(point.second,j));
                //tree::insert(v,u,get_dis(point.second,j));
                tree::con[++tree::tot].u=u;tree::con[tree::tot].v=v;tree::con[tree::tot].w=get_dis(point.se,node[j].id);
            }
        }
        tree::span();//跑一颗生成树
        int q;scanf("%d",&q);
        for(int i=1;i<=q;i++){
            int u,v;scanf("%d%d",&u,&v);
            printf("%d
    ",tree::lca(u,v));
        }
    }
    int main() {
        int t;scanf("%d",&t);
        while(t--){
    
            solve();
        }
    }
    

  • 相关阅读:
    FJNU 1151 Fat Brother And Geometry(胖哥与几何)
    FJNU 1157 Fat Brother’s ruozhi magic(胖哥的弱智术)
    FJNU 1159 Fat Brother’s new way(胖哥的新姿势)
    HDU 3549 Flow Problem(最大流)
    HDU 1005 Number Sequence(数列)
    Tickets(基础DP)
    免费馅饼(基础DP)
    Super Jumping! Jumping! Jumping!(基础DP)
    Ignatius and the Princess IV(基础DP)
    Keywords Search(AC自动机)
  • 原文地址:https://www.cnblogs.com/GavinZheng/p/11658171.html
Copyright © 2020-2023  润新知