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;
}

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

被折叠的 条评论
为什么被折叠?



