[UOJ#24]【IOI2014】Rail

时间:2024-08-23 15:03:56

#24. 【IOI2014】Rail


*有一个连接着岛的东、西两岸的庞大的铁路线。这个铁路线包含有 mm 个区段。这些相连的区段以数字 0,…,m−10,…,m−1 为编号,且编号由西端开始。每一个区段都包含一段在北面向西单行的路轨,以及一段在南面向东单行的路轨。部分区段还可能会有一个位于这两段路轨之间的车站。

区段可以分成三种类型。其中 C 类型的区段会有一个车站,而且只能从北面的路轨进入车站并且从南面的路轨离开。D 类型的区段也会有一个车站,而且只能从南面的路轨进入车站并且从北面的路轨离开。空 类型的区段则没有车站。举例来说,如下图所示,区段 00、44 和 66 是空类型区段,区段 11、22 和 33 是C类型,而区段 55 则是D类型。这些区段沿水平方向连接在一起。相邻区段的路轨之间以连接器彼此相连,在图中所示的灰色方块就是连接器。

[UOJ#24]【IOI2014】Rail

在这个铁路系统*有 nn 个车站,它们分别以数字 00 到 n−1n−1 为编号。这里假设我们可以由任何一个车站出发、沿着路轨的方向到达任何其他车站。例如我们可以由车站 00 到达车站 22,所经路线是由区段 22 开始,经区段 33 及区段 44 的南面路轨,然后经由区段 55 通过车站 11,再经区段 44 的北面路轨,最后到达区段 33 并由北面路轨进入车站 22。

由于从一个车站到另一个车站有多个不同的可行路线,我们定义由一个车站到另一个车站的距离为所有可行路线中所经的连接器的最少数量。例如由车站 00 到车站 22 的最短路线所经的区段是 2→3→4→5→4→32→3→4→5→4→3,所经的连接器数目为 55,因此距离为 55。

该铁路系统是由计算机系统管理的。不幸的是,在一次电力事故后,计算机系统丢失了各车站所在的区段编号及其相应的类型。目前仅知道车站 00 所在的区段编号,且该区段总是 C 类型。幸运的是计算机能够查询出任意两个车站之间的距离。例如计算机可以查询“车站 00 与车站 22 的距离”,并且得到答案为 55。

任务


你需要编写一个函数 findLocation,以确定每一个车站所在的区段编号及其相应的类型。

  • findLocation(n, first, location, stype)
    • n: 车站的数目
    • first: 车站 00 所在区段的编号。
    • location: 大小为 nn 的数组; 你需要将车站 ii 所在的区段编号存放到 location[i] 中。
    • stype: 大小为 nn 的数组;你需要将车站 ii 的类型存放到 stype[i] 中:其中 1 表示 C 类型,而 2 则表示 D 类型。

你可以调用一个函数 getDistance 以帮助你找出各车站的所在区段及其类型。

  • getDistance(i, j) 将返回车站 ii 到车站 jj 的距离。如果调用 getDistance(i, i) 将返回00。如果 ii 或 jj 在 0≤i,j≤n−10≤i,j≤n−1 范围之外,getDistance(i, j) 将返回 −1−1。

子任务


在所有的子任务中,区段的数目 mm 不会超过 10000001000000。在部分子任务中,调用 getDistance 的次数是受限的,且该限制将因子任务而异。如果调用次数超过相应限制,你的程序将被判为“wrong answer”。

子任务 分值 nn getDistance的调用次数 备注
1 8 1≤n≤1001≤n≤100 无限制 除车站 00 外,其他车站均在 D 类型区段中。
2 22 1≤n≤1001≤n≤100 无限制 所有在车站 00 东侧的车站均在 D 类型区段中,
而所有在车站 00 西侧的车站均在 C 类型区段中。
3 26 1≤n≤50001≤n≤5000 n(n−1)/2n(n−1)/2 无其他限定
4 44 1≤n≤50001≤n≤5000 3(n−1)3(n−1) 无其他限定

实现细节


本题只支持 C/C++。

你只能提交一个源文件实现如上所述的 findLocation 函数,并且遵循下面的命名和接口。你需要包含头文件 rail.h。

void findLocation(int n, int first, int location[], int stype[]);

函数getDistance的接口信息如下。

int getDistance(int i, int j);

评测方式


评测系统将读入如下格式的输入数据:

  1. 第 11 行: 子任务编号
  2. 第 22 行: nn
  3. 第 3+i3+i 行,(0≤i≤n−1)(0≤i≤n−1):stype[i](1表示C类型,2表示D类型), location[i]。

在 findLocation 返回后,如果你的程序计算出的 location[0] …… location[n-1] 和 stype[0] …… stype[n-1] 与输入数据一致,评测系统将输出 CorrectCorrect,否则输出 IncorrectIncorrect。

交互式类型的题目怎么本地测试

时间限制:3s3s

空间限制:256MB256MB

下载


样例及测评库下载

分析:


虽然说知道自己菜,但是好不容易有道题在uoj上rank1,怎么能不发篇题解纪念一下。
题意很简单,套路也很明显,首先看到询问次数是3(n - 1),这种一般是O(n - 1)用第0点找到最远/近点,然后用最远/近点再O(n - 1)扫一遍,最后再利用两次询问和第三次某种特殊查询得出答案。
这道题先O(n - 1)找出最近点,一定是距离0最近的在右边的D形车站记为A,记录d(0 ->i)距离为dis[i]
然后O(n - 2)用A扫一遍记录d(A->i)距离为Dis[i]
首先有个很明显的性质d(i -> j) == d(j -> i)
然后我们想办法把图分成三部分,0 到 A中间,0左边,0右边。
0 到A中间讨论起来很简单,dis[i] == dis[A] + Dis[i],且(Dis[i] < dis[A]),形状为C,位置为 location[A] - Dis[i]
0 左边 dis[i] == dis[A] + Dis[i] ,且(Dis[i] > dis[A])
A 右边 dis[i] != dis[A] + Dis[i]
为什么可以这么讨论,因为无论是0到A中间,0左边,无论转几次向,第一次转向必定经过A。
然后单独讨论0左边的。
0左边的C形车站位置为 location[A] - Dis[i]
0左边的D形车站位置为 location[A] - Dis[B] + d(B,i) ,B为其左边最近的C形车站。
用Dis数组排序后单调栈维护0左边的C形车站。
如果是个D形车站i,它一定在B右边,即排序后在B后面。
记录当前栈顶C形车站为C,记录i与C距离为tmp,
如图
[UOJ#24]【IOI2014】Rail

如果其为D形车站那么,C的位置为location[A]  - Dis[C],B的位置为location[A] - Dis[B] = location[C] + D,D为d(C,B)

通过推导 D = (Dis[C] + tmp - Dis[i] ) / 2,这个画图或者公式推都行。

然后就判断一下 location[C] + D,有没有一个C形车站(即B)就可以,判断有没有C形车站hash就行。

如果满足且location[C] + D < frist ,那么i为D形车站,位置为lcation[C] + tmp

如果不满足或者location[C]+ D >=first,那么i为C形车站,位置为location[A]   - Dis[i]

在A右边讨论同理

AC代码:


# include "rail.h"
# include <iostream>
# include <cstring>
# include <algorithm>
using namespace std;
const int N = 5e3 + ;
const int mod = 2e5 + ;
struct node{
int dis,id;
bool operator <(const node & other)const{return dis < other.dis;}
}l[N],r[N];
int dis[N],Dis[N],t1,t2,Amx = 1e9,A,que[N],tmp,d,t,hs[mod];
void add(int p)
{
int x = p % mod;
while(hs[x] != -)
{
x++;
if(x == mod)x = ;
}
hs[x] = p;
}
bool find(int p)
{
int x = p % mod;
if(x < )return false;
while(hs[x] != -)
{
if(hs[x] == p)return true;
x++;
if(x == mod)x = ;
}
return false;
}
void findLocation(int n, int first, int location[], int stype[])
{
memset(hs,-,sizeof hs);
location[] = first;stype[] = ;
if(n == )return;
for(int i = ;i < n;i++)dis[i] = getDistance(,i),A = dis[i] < Amx ? i : A,Amx = dis[i] < Amx ? dis[i] : Amx;
for(int i = ;i < n;i++)if(i != A)Dis[i] = getDistance(A,i);
location[A] = first + dis[A];stype[A] = ;
for(int i = ;i < n;i++)
{
if(i == || i == A)continue;
if(dis[A] + Dis[i] == dis[i] && Dis[i] < dis[A])location[i] = location[A] - Dis[i],stype[i] = ;
if(dis[A] + Dis[i] == dis[i] && Dis[i] > dis[A])l[++t1] = (node){Dis[i],i};
if(dis[A] + Dis[i] != dis[i])r[++t2] = (node){dis[i],i};
}
sort(l + ,l + t1 + );sort(r + ,r + t2 + );
if(t2)
{
location[que[t = ] = r[].id] = first + r[].dis;stype[r[].id] = ;add(location[r[].id]);
for(int i = ;i <= t2;i++)
{
tmp = getDistance(r[i].id,que[t]);
d = (dis[que[t]] + tmp - dis[r[i].id]) / ;
if(location[que[t]] - d > location[A] && find(location[que[t]] - d))
location[r[i].id] = location[que[t]] - tmp,stype[r[i].id] = ;
else location[r[i].id] = first + r[i].dis,stype[r[i].id] = ,add(location[r[i].id]),que[++t] = r[i].id; }
}
if(t1)
{
location[que[t = ] = l[].id] = location[A] - l[].dis;stype[l[].id] = ;add(location[l[].id]);
for(int i = ;i <= t1;i++)
{
tmp = getDistance(l[i].id,que[t]);
d = (Dis[que[t]] + tmp - Dis[l[i].id]) / ;
if(location[que[t]] + d < first && find(location[que[t]] + d))
location[l[i].id] = location[que[t]] + tmp,stype[l[i].id] = ;
else location[l[i].id] = location[A] - l[i].dis,stype[l[i].id] = ,add(location[l[i].id]),que[++t] = l[i].id;
}
}
}