【IOI2014】Rail

题面

分析

首先会想到,询问一下0到其他车站的距离。容易发现距离最近的一定是0右边的第一个 \(D\) 类车站,设为 \(p\)
image

看看我们此时得到了什么?在0到 \(p\) 车站之间的车站一定是 \(C\) 类车站(\(p\) 是0右边的第一个 \(D\) 类)。还有其他的吗?好像没有了。

所以我们试图问一下 \(p\) 到其他车站的距离以增加条件。

\(c\) 车站为 \(p\) 左边的C类(无论是否在0左边),有:\(dis_{0,c}=dis_{p,c}+dis_{0,p}\) 。如图:
image

若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\) 车站的答案,有两种情况 :
image

若x为D类,若询问一下 \(a\)\(x\) 的距离,那肯定是 \(a\)\(mid\) ,再从 \(mid\)\(x\)

若x为C类:image

\(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;
		}
	}
	//基本同上 
}
posted @ 2021-11-03 16:05  keepcoder  阅读(115)  评论(1编辑  收藏  举报