【IOI2014】Rail
分析
首先会想到,询问一下0到其他车站的距离。容易发现距离最近的一定是0右边的第一个 \(D\) 类车站,设为 \(p\)。
看看我们此时得到了什么?在0到 \(p\) 车站之间的车站一定是 \(C\) 类车站(\(p\) 是0右边的第一个 \(D\) 类)。还有其他的吗?好像没有了。
所以我们试图问一下 \(p\) 到其他车站的距离以增加条件。
若 \(c\) 车站为 \(p\) 左边的C类(无论是否在0左边),有:\(dis_{0,c}=dis_{p,c}+dis_{0,p}\) 。如图:
若c为D类,显然有 \(dis_{0,c} \ge dis_{p,c}+dis_{0,p}\) 。于是可以确定一个车站是否在 \(p\)左边。右边同理可得。
现在的问题就只有如何确定每个车站是C还是D了(确定方向可用任意一个dis算真正位置)。
那我们假设 \(p\) 右边的全是D类,把 \(p\) 右边的按到 \(p\) 的距离排序后从左往右扫。设上一次确定的一个 \(D\) 类车站为 \(a\) ,从0到 \(a\) 之间的车站已确定,现在想求 \(x\) 车站的答案,有两种情况 :
若x为D类,若询问一下 \(a\) 到 \(x\) 的距离,那肯定是 \(a\) 到 \(mid\) ,再从 \(mid\) 到 \(x\) 。
若x为C类:
则 \(x\) 到 \(a\) 就是直接走的距离,但此时 \(mid\) 必须为D类,否则 \(dis_{0,x}\) 将变大,则出现矛盾。
容易发现这两种情况的 \(x\) 是关于 \(mid\) 对称的,而一个点只有一个车站。又因为已知 \(dis_{0,x}\) 和 \(dis_{p,x}\) 用一些几何方法求出 \(mid\) 的位置,再判断类型即可。
时间复杂度:\(O(nlogn)\)。
code
#include<bits/stdc++.h>
#include"rail.h"
using namespace std;
const int N=5000,INF=(1<<30),M=1e6;
int dis0[N+5],disp[N+5],pos;
int que[N+5],t,tag[M+5],t1,t2;
//当处理右边时,q用于存上一次确定的D类
struct node
{
int dis,id;
bool operator < (const node &it) const
{
return dis<it.dis;
}
} l[N+5],r[N+5];
void add(int p) {tag[p]=1;}
int find(int p) {return tag[p]!=-1;}
void findLocation(int n, int first, int location[], int stype[])
{
memset(tag,-1,sizeof tag);
location[0]=first;stype[0]=1;
if(n==1) return;dis0[0]=INF;
for(int i=1; i<n; i++)
{
dis0[i]=getDistance(0,i);
if(dis0[i]<dis0[pos]) pos=i;
}
for(int i=1; i<n; i++) if(i!=pos) disp[i]=getDistance(pos,i);
location[pos]=first+dis0[pos];stype[pos]=2;
for(int i=0; i<n; i++)
{
if(i==0||i==pos) continue;
if(dis0[pos]+disp[i]==dis0[i]&&disp[i]<dis0[pos]) location[i]=location[pos]-disp[i],stype[i]=1;
//[0,p]中间的C类可以直接确定
if(dis0[pos]+disp[i]==dis0[i]&&disp[i]>dis0[pos]) l[++t1]=node{disp[i],i};
//此时一定在0左边。
if(dis0[pos]+disp[i]!=dis0[i]) r[++t2]=node{dis0[i],i};
//在p右边
}
sort(l+1,l+1+t1);sort(r+1,r+1+t2);
if(t2)
{
location[que[t=1]=r[1].id]=first+r[1].dis;stype[r[1].id]=2;
tag[location[r[1].id]]=1;
for(int i=2; i<=t2; i++)
{
int tmp=getDistance(r[i].id,que[t]);
int d=(dis0[que[t]]+tmp-dis0[r[i].id])/2;
//求对称点相对q[t]的位置
if(location[que[t]]-d>location[pos]&&tag[location[que[t]]-d]!=-1)
//对称点为D,则这个站为C
location[r[i].id]=location[que[t]]-tmp,stype[r[i].id]=1;
else location[r[i].id]=first+r[i].dis,stype[r[i].id]=2,tag[location[r[i].id]]=1,que[++t]=r[i].id;
}
}
//处理右边
if(t1)
{
location[que[t=1]=l[1].id]=location[pos]-l[1].dis;stype[l[1].id]=1;
tag[location[l[1].id]]=1;
for(int i=2; i<=t1; i++)
{
int tmp=getDistance(l[i].id,que[t]);
int d=(disp[que[t]]+tmp-disp[l[i].id])/2;
if(location[que[t]]+d<first&&tag[location[que[t]]+d]!=-1)
location[l[i].id]=location[que[t]]+tmp,stype[l[i].id]=2;
else location[l[i].id]=location[pos]-l[i].dis,stype[l[i].id]=1,tag[location[l[i].id]]=1,que[++t]=l[i].id;
}
}
//基本同上
}