zoukankan      html  css  js  c++  java
  • [算法模版]曼哈顿距离最小生成树

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

    算法描述

    先说结论。做法就是把平面等分成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();
        }
    }
    

  • 相关阅读:
    中科燕园GIS外包--移动GIS
    创建二维数组
    DSP开发中遇到的问题
    Unity 2D游戏开发教程之精灵的死亡和重生
    Unity 2D游戏开发教程之摄像头追踪功能
    Unity 2D游戏开发教程之2D游戏的运行效果
    Unity 2D游戏开发教程之使用脚本实现游戏逻辑
    Kail Linux渗透测试教程之免杀Payload生成工具Veil
    Kail Linux渗透测试教程之在Metasploit中扫描
    Kail Linux渗透测试教程之网络扫描和嗅探工具Nmap
  • 原文地址:https://www.cnblogs.com/GavinZheng/p/11658171.html
Copyright © 2011-2022 走看看