题目链接:https://www.luogu.com.cn/problem/P1081
此题难点在于预处理(难想到)和一些细节。如果我们暴力找最近和次近的点时间复杂度为O(n^2),肯定超时。所以我们要换个方法。这里用双向链表,我们将每个点放进链表,按海拔从低到高排序。然后按从西往东的顺序,一个一个拿出来,它的最近点和次近点,就是 i-2 i-1 i+1 i+2 里面的。更新完这个点到最近和次近距离之后把这个点从链表钟删去,这样能把复杂度优化到O(nlogn)。然后我们再用倍增,处理每个点。最后输出答案。
代码如下
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
const int inf=0xfffffff;
struct node
{
ll height;//海拔
int last,next;//前驱与后继
int id;//排序前编号
}p[maxn];
int pos[maxn];//排序后的位置
ll dis1[maxn];//距离i最近的距离
ll dis2[maxn];//距离i次近的距离
int to1[maxn];//距离i最近的城市
int to2[maxn];//距离i次近的城市
bool cmp(node a,node b)
{
return a.height < b.height;
}
int n,m;
void modify(int i,int loc,int x)
{
if(x<1||x>n)
return ;
if(!dis1[i]||dis1[i]>abs(p[loc].height-p[x].height)||(dis1[i]==abs(p[loc].height-p[x].height)
&&p[pos[to1[i]]].height>p[x].height))
{
dis2[i]=dis1[i];
to2[i]=to1[i];
dis1[i]=abs(p[loc].height-p[x].height);
to1[i]=p[x].id;
}
else if(!dis2[i]||dis2[i]>abs(p[loc].height-p[x].height)||(dis2[i]==
abs(p[loc].height-p[x].height)&&p[pos[to2[i]]].height>p[x].height))
{
dis2[i]=abs(p[loc].height-p[x].height);
to2[i]=p[x].id;
}
}
ll dis4[maxn][21];//i交换驾驶2^j次a走的距离
ll dis5[maxn][21];//i交换驾驶2^j次b走的距离
ll dis6[maxn][21];//i交换驾驶2^j次a与b一共走的距离
int to3[maxn][21];//i交换驾驶2^j次到达的城市
void init()//倍增初始化
{
for(int i=1;i<=n;i++)
{
dis4[i][0]=dis2[i];
dis5[i][0]=dis1[to2[i]];
dis6[i][0]=dis2[i]+dis1[to2[i]];
to3[i][0]=to1[to2[i]];
}
}
void Double()//倍增
{
for(int i=1;i<=20;i++)
for(int j=1;j<=n;j++)
{
to3[j][i]=to3[to3[j][i-1]][i-1];
if(to3[j][i])
{
dis4[j][i]=dis4[j][i-1]+dis4[to3[j][i-1]][i-1];
dis5[j][i]=dis5[j][i-1]+dis5[to3[j][i-1]][i-1];
dis6[j][i]=dis6[j][i-1]+dis6[to3[j][i-1]][i-1];
}
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld",&p[i].height);
p[i].id=i;
}
sort(p+1,p+1+n,cmp);
for(int i=1;i<=n;i++)
{
if(i!=1)
p[i].last=i-1;
if(i!=n)
p[i].next=i+1;
}
for(int i=1;i<=n;i++)
pos[p[i].id]=i;
for(int i=1;i<=n;i++)
{
int t=pos[i];
modify(i,t,p[p[t].next].next);
modify(i,t,p[p[t].last].last);
modify(i,t,p[t].next);
modify(i,t,p[t].last);
if(p[t].next)
p[p[t].next].last=p[t].last;
if(p[t].last)
p[p[t].last].next=p[t].next;
p[t].last=0;
p[t].next=0;
}
init();
Double();
ll x0,x;
int s;
scanf("%lld",&x0);
double Mi=inf;
int t=0;
for(int i=1;i<=n;i++)
{
ll a=0,b=0,tx;
tx=x0;
int y=i;
for(int j=20;j>=0;j--)
{
if(dis6[y][j]&&dis6[y][j]<=tx)
{
tx-=dis6[y][j];
a+=dis4[y][j];
b+=dis5[y][j];
y=to3[y][j];
}
}
if(dis2[y]<=tx)
a+=dis2[y];
if(a<=0)
continue;
if(!t||(double)a/(double)b-Mi<-0.00000001
||(fabs((double)a/(double)b-Mi)<=0.00000001&&p[pos[t]].height<p[pos[i]].height))
{
Mi=(double)a/(double)b;
t=i;
}
}
printf("%d\n",t);
scanf("%d",&m);
while(m--)
{
ll a=0,b=0;
scanf("%d %lld",&s,&x);
for(int i=20;i>=0;i--)
{
if(dis6[s][i]&&dis6[s][i]<=x)
{
x-=dis6[s][i];
a+=dis4[s][i];
b+=dis5[s][i];
s=to3[s][i];
}
}
if(dis2[s]<=x)
a+=dis2[s];
printf("%lld %lld\n",a,b);
}
//for(int i=0;i<=20;i++)
//for(int j=1;j<=n;j++)
//printf("%lld\n",dis4[j][i]);
return 0;
}