bzoj4449 [Neerc2015]Distance on Triangulation

本文介绍了一种解决多边形三角剖分下两点间最短路径问题的方法,利用分治策略通过选择合适的对角线进行递归求解,并通过BFS算法确定不同情况下的最短路径。

Description


给定一个正 n 边形及其三角剖分, 共 2n − 3 条边 (n 条多边形的边和 n − 3 条对角线), 每条边的长度为 1.
共 q 次询问, 每次询问给定两个点, 求它们的最短距离.

对于前 5% 的数据, n ≤ 10;
对于前 15% 的数据, n ≤ 1000;
对于前 25% 的数据, n ≤ 5000;
对于前 35% 的数据, n ≤ 10000;
对于前 50% 的数据, n ≤ 20000;
对于前 75% 的数据, n ≤ 35000;
对于 100% 的数据, 1 ≤ n ≤ 52000,1 ≤ q ≤ 2n.

Solution


这道题目说明了一个道理,那就是分治不一定需要合并答案

由于给出的图是一个多边形的三角剖分, 因此每条对角线都将这个多边形分成了两部分。考虑分治,每次在选出一条对角线使得两部分点数尽可能均匀, 递归求解

对于一个询问(p,q),设所选对角线为(a,b),考虑当他们同时在选出对角线的一侧的情况,分别在选出对角线的两侧的情况。在同一侧的情况可以递归求,在两侧就以a和b为起点bfs,不难发现p到q的路径一定经过a、b中至少一个,取min即可

一开始写的时候非常naive的把max打成了min,100s的题目卡得我有点愧疚
据说可以转成对偶图然后点分治,反正我不会写

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define fill(x,t) memset(x,t,sizeof(x))

const int INF=0x7fffffff;
const int N=2000005;
const int E=4000005;

struct Q {int st,ed,id;} t[N],q[N],g1[N],g2[N];
struct edge {int x,y,next;} e[E];

int dis[2][N],queue[N];
int ls[N],edCnt;
int ans[N],a[N],g3[N],g4[N];
int n,m,tot;

int read() {
    int x=0,v=1; char ch=getchar();
    for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
    for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
    return x*v;
}

void add_edge(int x,int y) {
    e[++edCnt]=(edge) {x,y,ls[x]}; ls[x]=edCnt;
    e[++edCnt]=(edge) {y,x,ls[y]}; ls[y]=edCnt;
}

int find(int l,int r,int x) {
    int ret,mid;
    for (;l<=r;(a[mid=(l+r)/2]>=x)?(r=mid-1):(l=mid+1));
    return r+1;
}

void bfs(int st,int l,int r,int *dis) {
    rep(i,l,r) dis[a[i]]=INF; dis[st]=0;
    int head=1,tail=0;
    queue[++tail]=st;
    while (head<=tail) {
        int now=queue[head++];
        for (int i=ls[now];i;i=e[i].next) {
            if (dis[now]+1<dis[e[i].y]&&a[find(l,r,e[i].y)]==e[i].y) {
                dis[e[i].y]=dis[now]+1;
                queue[++tail]=e[i].y;
            }
        }
    }
}

int get_ans(Q now) {
    int ret=INF;
    ret=std:: min(ret,dis[0][now.st]+dis[0][now.ed]);
    ret=std:: min(ret,dis[1][now.st]+dis[1][now.ed]);
    ret=std:: min(ret,dis[0][now.st]+1+dis[1][now.ed]);
    ret=std:: min(ret,dis[1][now.st]+1+dis[0][now.ed]);
    return ret;
}

void solve(int l1,int r1,int l2,int r2,int l3,int r3) {
    if (l3>r3) return ;
    int rec,mn=INF;
    rep(i,l1,r1) {
        int x=find(l2,r2,t[i].st),y=find(l2,r2,t[i].ed);
        if (x>y) std:: swap(x,y);
        int tmp=std:: max(y-x+1,r2-l2+1-(y-x));
        if (tmp<mn) {
            mn=tmp;
            rec=i;
        }
    }
    bfs(t[rec].st,l2,r2,dis[0]);
    bfs(t[rec].ed,l2,r2,dis[1]);
    int cnt1=0,cnt2=0;
    rep(i,l3,r3) {
        if (q[i].st==t[rec].st&&q[i].ed==t[rec].ed) {
            ans[q[i].id]=1;
            continue;
        }
        ans[q[i].id]=std:: min(ans[q[i].id],get_ans(q[i]));
        if (q[i].st>t[rec].st&&q[i].st<t[rec].ed&&q[i].ed>t[rec].st&&q[i].ed<t[rec].ed) {
            g1[++cnt1]=q[i];
        } else if ((q[i].st<t[rec].st||q[i].st>t[rec].ed)&&(q[i].ed>t[rec].ed||q[i].ed<t[rec].st)) {
            g2[++cnt2]=q[i];
        }
    }
    rep(i,1,cnt1) q[l3+i-1]=g1[i];
    rep(i,1,cnt2) q[l3+cnt1+i-1]=g2[i];
    int cnt3=0,cnt4=0;
    rep(i,l2,r2) {
        if (t[rec].st<=a[i]&&a[i]<=t[rec].ed) g3[++cnt3]=a[i];
        if (t[rec].st>=a[i]||a[i]>=t[rec].ed) g4[++cnt4]=a[i];
    }
    rep(i,1,cnt3) a[l2+i-1]=g3[i];
    rep(i,1,cnt4) a[l2+cnt3+i-1]=g4[i];
    int cnt5=0,cnt6=0;
    rep(i,l1,r1) if (i!=rec) {
        if (t[i].st>=t[rec].st&&t[i].ed<=t[rec].ed) {
            g1[++cnt5]=t[i];
        } else g2[++cnt6]=t[i];
    }
    rep(i,1,cnt5) t[l1+i-1]=g1[i];
    rep(i,1,cnt6) t[l1+cnt5+i-1]=g2[i];
    solve(l1+cnt5,l1+cnt5+cnt6-1,l2+cnt3,l2+cnt3+cnt4-1,l3+cnt1,l3+cnt1+cnt2-1);
    solve(l1,l1+cnt5-1,l2,l2+cnt3-1,l3,l3+cnt1-1);
}

int main(void) {
    n=read();
    rep(i,1,n) a[i]=i;
    rep(i,1,n-3) {
        int st=read(),ed=read();
        if (st>ed) std:: swap(st,ed);
        add_edge(st,ed);
        t[i]=(Q) {st,ed};
    }
    rep(i,1,n) add_edge(i,i%n+1);
    m=read();
    rep(i,1,m) ans[i]=INF;
    rep(i,1,m) {
        int st=read(),ed=read();
        if (st>ed) std:: swap(st,ed);
        if (st==ed) ans[i]=0;
        else if (st%n+1==ed||ed%n+1==st) ans[i]=1;
        else q[++tot]=(Q) {st,ed,i};
        ans[i]=std:: min(ans[i],std:: min(ed-st,st+n-ed));
    }
    solve(1,n-3,1,n,1,tot);
    rep(i,1,m) printf("%d\n", ans[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值