1. 程式人生 > 其它 >【IOI2014】Rail

【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;
		}
	}
	//基本同上 
}