[NEERC2015]Distance on Triangulation

考虑多边形三角剖分的实际意义,即一条对角线将一个多边形分为两个。很容易想到分治:每次在当前多边形中选出一条对角线使得两部分点数尽可能均匀,直到当前多边形为三角形为止。

询问时若两个点在选定的对角线同侧,则递归求解。若在两侧,由于三角剖分的性质,不会有任何一条边穿过此对角线。因此两点间最短路必然穿过对角线端点中的一个。bfs计算每个点到对角线距离即可。

细节注释在代码里

  1 #include<bits/stdc++.h>
  2 #define ll long long
  3 #define ld long double
  4 using namespace std;
  5 const int N=1002005;
  6 int head[N],dis[2][N],p[N],b[N],h1[N],h2[N],can[N],ans[N],num,cnt,n,m;
  7 struct asd{
  8     int next,to;
  9 }a[N];
 10 struct edge{
 11     int x,y;
 12 }e[N],t1[N],t2[N];
 13 struct Q{
 14     int x,y,id;
 15 }q[N],f1[N],f2[N];
 16 void add(int x,int y){
 17     a[++num].next=head[x];
 18     head[x]=num;
 19     a[num].to=y;
 20 }
 21 void bfs(int s,int l,int r,int *dis){
 22     cnt++;
 23     for(int i=l;i<=r;i++) dis[p[i]]=0x3f3f3f3f,can[p[i]]=cnt;//打标记,只需遍历当前多边形上的点即可
 24     dis[s]=0; queue<int>q; q.push(s);
 25     while(!q.empty()){
 26         int k=q.front();q.pop();
 27         for(int i=head[k];i;i=a[i].next){
 28             if(dis[a[i].to]==0x3f3f3f3f&&can[a[i].to]==cnt){
 29                 dis[a[i].to]=dis[k]+1;
 30                 q.push(a[i].to);
 31             }
 32         }
 33     }
 34 }
 35 void solve(int pl,int pr,int el,int er,int ql,int qr){//  p:点,e:边,q:询问
 36     if(ql>qr||el>er||pl>pr) return ;
 37     int num=0,now=0,Max=0x3f3f3f3f;
 38     for(int i=pl;i<=pr;i++)
 39         b[p[i]]=++num;                               // 将多边形上的点离散化
 40     for(int i=el;i<=er;i++){
 41         int k=b[e[i].y]-b[e[i].x]+1;
 42         if(max(k,pr-pl+3-k)<Max){
 43             Max=max(k,pr-pl+3-k);
 44             now=i;
 45         }
 46     }                                               //找分割边
 47     bfs(e[now].x,pl,pr,dis[0]);bfs(e[now].y,pl,pr,dis[1]);
 48     int n1=0,n2=0;
 49     for(int i=ql;i<=qr;i++){
 50         if(q[i].x>e[now].x&&q[i].y<e[now].y) f1[++n1]=q[i];
 51         else if((q[i].x<e[now].x||q[i].x>e[now].y)&&(q[i].y<e[now].x||q[i].y>e[now].y)) f2[++n2]=q[i];
 52         else ans[q[i].id]=min(ans[q[i].id],min(dis[1][q[i].x]+dis[1][q[i].y],dis[0][q[i].x]+dis[0][q[i].y]));
 53     }
 54     for(int i=1;i<=n1;i++) q[ql+i-1]=f1[i];
 55     for(int i=1;i<=n2;i++) q[ql+n1+i-1]=f2[i];
 56     int n3=0,n4=0,k=p[pr+1],l=p[pr+2];             //对角线端点重复计算在两个多边形里,覆盖掉p[pr+1],p[pr+2]的值,待会要回溯
 57     for(int i=pl;i<=pr;i++){
 58         if(p[i]>=e[now].x&&p[i]<=e[now].y)
 59             h1[++n3]=p[i];
 60         if(p[i]<=e[now].x||p[i]>=e[now].y)
 61             h2[++n4]=p[i];
 62     }
 63     for(int i=1;i<=n3;i++) p[pl+i-1]=h1[i];
 64     for(int i=1;i<=n4;i++) p[pl+n3+i-1]=h2[i];
 65     int n5=0,n6=0;
 66     for(int i=el;i<=er;i++){
 67         if(i==now) continue;
 68         if(e[i].x>=e[now].x&&e[i].y<=e[now].y) t1[++n5]=e[i];
 69         else t2[++n6]=e[i];
 70     }
 71     for(int i=1;i<=n5;i++) e[el+i-1]=t1[i];
 72     for(int i=1;i<=n6;i++) e[el+n5+i-1]=t2[i];
 73     solve(pl,pl+n3-1,el,el+n5-1,ql,ql+n1-1);
 74     solve(pl+n3,pl+n3+n4-1,el+n5,el+n5+n6-1,ql+n1,ql+n1+n2-1);
 75     p[pr+1]=k;p[pr+2]=l;                          //回溯
 76 }
 77 int main(){
 78     int i,j,k,l;
 79     freopen("bsh.in","r",stdin);
 80     freopen("bsh.out","w",stdout);
 81     scanf("%d",&n);
 82     for(i=1;i<=n;i++) p[i]=i;
 83     for(i=1;i<n;i++) add(i,i+1),add(i+1,i);
 84     add(1,n),add(n,1);
 85     for(i=1;i<=n-3;i++){
 86         scanf("%d%d",&k,&l);
 87         if(k>l) swap(k,l);
 88         add(k,l);add(l,k);
 89         e[i].x=k;
 90         e[i].y=l;
 91     }
 92     scanf("%d",&m);
 93     for(i=1;i<=m;i++){
 94         scanf("%d%d",&k,&l);
 95         if(k>l) swap(k,l);
 96         q[i].x=k;
 97         q[i].y=l;
 98         q[i].id=i;
 99         ans[i]=min(l-k,n-(l-k));                   //若k,l为同一个点(且此点不为三角划分的端点),分治不会处理
100     }                                              //此时ans=0,不赋值也可过,这里是为了保险
101     solve(1,n,1,n-3,1,m);
102     for(i=1;i<=m;i++)
103         printf("%d
",ans[i]);
104     return 0;
105 }
原文地址:https://www.cnblogs.com/jstcao/p/14602891.html