[NOIP2012]开车旅行

step1

设两个数组 \(A\) , \(B\) ,
\(A_i\) , \(B_i\) 表示在 \(i\) 城市时,小 \(A\) 和小 \(B\) 分别会选择去哪个点。
考虑如何预处理这两个数组。
观察到两个城市之间的距离就是两个城市海拔之差的绝对值,所以可以先根据每个城市的海拔进行排序。
排完序后, \(A_i\) , \(B_i\) 其实就是从以 \(i-1\) , \(i+1\) , \(i-2\) , \(i+2\) 这四个数为下标的海拔中找出离当前点第二近的和最近的点。
但是,由于编号较小的城市在编号较大的城市的西边,所以为了让车不往回开还需要把已经处理好的点从数列中删除。
故可以用双向链表实现。
这块的复杂度为 \(O(n \log n)\) 。

step2

设数组 \(f\) , \(f_{i,j}\) 表示两人从 \(i\) 城市出发轮流开 \(2^j\) 天会到哪个点。
设数组 \(FA\) , \(FB\) ,分别表示小 \(A\) 和小 \(B\)从 \(i\) 城市出发轮流开 \(2^j\) 天各会走多少路程。
先预处理 \(f\) 。
初值
\(f_{i,0}\) 就是从 \(i\) 城市出发,两人各走一步所到的城市。
即 \(f_{i,0}=B_{A_i}\) 。
转移
因为两个人在每个点所会去的城市都是固定的,所以可以直接转移。
\(f_{i,j}=f_{f_{i,j-1} \ \ ,j-1}\) 。
接下来,预处理 \(FA\) , \(FB\) 。
为了方便,用 \(dis(i,j)\) 表示 \(i\) 城市到 \(j\) 城市的距离。
初值
比较显然。
\(FA_{i,0}=dis(i,A_i)\) ,
\(FB_{i,0}=dis(A_i,B_{A_i})\) 。
转移
和 \(f\) 类似,也可以直接转移。
\(FA_{i,j}=FA_{i,j-1}+FA_{f_{i,j-1} \ \ ,j-1}\) ,
\(FB_{i,j}=FB_{i,j-1}+FB_{f_{i,j-1} \ \ ,j-1}\) 。
这块的复杂度也是 \(O(n \log n)\) 。

step3

处理询问。
第一问其实是和第二问类似的,只要枚举出发点即可。故下面只考虑第二问。
可以把小 \(A\) 和小 \(B\) 并在一起处理,和普通的倍增处理类似。
但是,由于小 \(A\) 是先走的,所以最后还要判断一下小 \(A\) 能不能再走一步。
第一问的复杂度为 \(O(n \log n)\) ,第二问的复杂度为 \(O(m \log n)\) 。
总复杂度为 \(O(n \log n+m \log n)\) ,可以通过此题。

code

我的代码写的有亿点丑陋,但还是放一下吧。

#include<algorithm>
#include<iostream>
#include<cstdio>
#define int long long
using namespace std;
const int N=1e5+10;
const double inf=1e18;
int n,a[N];
int A[N],B[N];
int f[N][20]; 
int FA[N][20],FB[N][20];
int X,Y;
struct node
{
	int id,num;
	int pre,nxt;
}b[N];
bool cmp(node x,node y)
{
	return x.num<y.num;
}
bool CMP(node x,node y)
{
	return x.id<y.id;
}
int ABS(int x)
{
	return x>0?x:-x;
}
int get(int x,int y)
{
	return ABS(b[x].num-b[y].num);
}
void GET(int x,int y)
{
	X=0,Y=0;
	for(int i=19;i>=0;i--)
		if(f[x][i]&&X+Y+FA[x][i]+FB[x][i]<=y)
			X+=FA[x][i],Y+=FB[x][i],x=f[x][i];
	if(A[x]&&X+Y+FA[x][0]<=y) X+=FA[x][0];
} 
signed main()
{
	scanf("%lld",&n);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
	b[0].num=inf;
	for(int i=1;i<=n;i++)
		b[i].id=i,b[i].num=a[i];
	sort(b+1,b+n+1,cmp);
	for(int i=1;i<=n;i++)
		b[i].pre=b[i-1].id,b[i].nxt=b[i+1].id;
	b[1].pre=0,b[n].nxt=0;
	sort(b+1,b+n+1,CMP);
	for(int i=1;i<=n;i++)
	{
		if(!b[i].pre)
		{
			B[i]=b[i].nxt;
			A[i]=b[b[i].nxt].nxt;
			b[b[i].nxt].pre=b[i].pre;
			continue;
		}
		if(!b[i].nxt)
		{
			B[i]=b[i].pre;
			A[i]=b[b[i].pre].pre;
			b[b[i].pre].nxt=b[i].nxt;
			continue;
		}
		if(get(i,b[i].pre)<=get(i,b[i].nxt))
		{
			B[i]=b[i].pre;
			if(get(i,b[b[i].pre].pre)<=get(i,b[i].nxt)) A[i]=b[b[i].pre].pre;
			else A[i]=b[i].nxt;
		}
		else
		{
			B[i]=b[i].nxt;
			if(get(i,b[i].pre)<=get(i,b[b[i].nxt].nxt)) A[i]=b[i].pre;
			else A[i]=b[b[i].nxt].nxt;
		}
		b[b[i].nxt].pre=b[i].pre;
		b[b[i].pre].nxt=b[i].nxt;
	}
	for(int i=1;i<=n;i++) f[i][0]=B[A[i]];
	for(int j=1;j<20;j++)
		for(int i=1;i<=n;i++) f[i][j]=f[f[i][j-1]][j-1];
	for(int i=1;i<=n;i++)
	{
		FA[i][0]=get(i,A[i]);
		FB[i][0]=get(A[i],B[A[i]]);
	}
	for(int j=1;j<20;j++)
		for(int i=1;i<=n;i++)
		{
			FA[i][j]=FA[i][j-1]+FA[f[i][j-1]][j-1];
			FB[i][j]=FB[i][j-1]+FB[f[i][j-1]][j-1];
		}
	int x,s=0;
	double ans=inf;
	scanf("%lld",&x);
	for(int i=1;i<=n;i++)
	{
		GET(i,x);
		if(Y==0)
		{
			if(ans==inf&&a[i]>a[s]) s=i;
		}
		else
		{
			if(ans>(double)(X)/(double)(Y)) ans=(double)(X)/(double)(Y),s=i;
			else if(ans==(double)(X)/(double)(Y)&&a[i]>a[s]) s=i;
		}
	}
	printf("%lld\n",s);
	int m;
	scanf("%lld",&m);
	while(m--)
	{
		scanf("%lld%lld",&s,&x);
		GET(s,x);
		printf("%lld %lld\n",X,Y);
	}
	return 0;
}
上一篇:【NOIP2012提高组】开车旅行


下一篇:NOIP2012提高 国王游戏题解