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

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

算法描述

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

对于在以\(i\)为坐标原点\(y\)轴正半轴右边那个区域(右上角),区域内离他最近的点\(j\)满足\(y_j-x_j\geq 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\n",tree::lca(u,v));
    }
}
int main() {
    int t;scanf("%d",&t);
    while(t--){

        solve();
    }
}

posted @ 2019-10-12 07:29  GavinZheng  阅读(333)  评论(0编辑  收藏  举报