[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
",s);
	int m;
	scanf("%lld",&m);
	while(m--)
	{
		scanf("%lld%lld",&s,&x);
		GET(s,x);
		printf("%lld %lld
",X,Y);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/zhs1/p/14009095.html