题目:
https://www.luogu.org/problem/show?pid=1081
分析:
这题第一眼给人的感觉就是要模拟,模拟两人交替开车,分别预处理出离特定城市第一近和第二近的(用set)。实际上就是这样,只不过用set和倍增优化了一下,用:
g[i][k]表示从位置i开始,两人轮流开2^k轮车最后到达的位置;
f[i][j][0] 表示表示从位置i开始,两人轮流开2^k轮车最后小A走过的距离;
f[i][j][1] 表示表示从位置i开始,两人轮流开2^k轮车最后小B走过的距离;
容易得到:
nxt[i][1]表示离i第二近的城市编号
nxt[i][0]表示离i第一近的城市编号
dis[i][1]表示离i第二近的城市编号
dis[i][0]离i第一近的城市编号
初始化:
g[i][0]=nxt[nxt[i][1]][0]; //从i开始两人轮流开一轮到的地方
f[i][0][0]=dis[i][1]; //开一次b走的路径
f[i][0][1]=dis[nxt[i][1]][0]; //开一次a走的路径
递推:
g[i][j]=g[g[i][j-1]][j-1]; //从第一近到第二近
f[i][j][0]=f[i][j-1][0]+f[g[i][j-1]][j-1][0]; //A走一轮,再走一轮
f[i][j][1]=f[i][j-1][1]+f[g[i][j-1]][j-1][1]; //B走一轮,再走一轮
下面是参考代码:
#include <iostream>View Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <set>
#define maxn 100000+10
using namespace std;
struct point
{
int num,h;
bool operator < (const point ano) const
{
return h<ano.h;
}
}city[maxn];
set<point> S;
set<point> :: iterator it;
long long f[maxn][21][2];
int nxt[maxn][2],dis[maxn][2],g[maxn][21];
int n,x0,m;
void update(point x,point y)
{
if(!nxt[x.num][0])
{
nxt[x.num][0]=y.num;
dis[x.num][0]=abs(x.h-y.h);
}
else if(dis[x.num][0]>abs(x.h-y.h)||(dis[x.num][0]==abs(x.h-y.h)&&y.h<city[nxt[x.num][0]].h))
{
nxt[x.num][1]=nxt[x.num][0];
dis[x.num][1]=dis[x.num][0];
nxt[x.num][0]=y.num;
dis[x.num][0]=abs(x.h-y.h);
}
else if(dis[x.num][1]>abs(x.h-y.h)||(dis[x.num][1]==abs(x.h-y.h)&&y.h<city[nxt[x.num][1]].h))
{
nxt[x.num][1]=y.num;
dis[x.num][1]=abs(x.h-y.h);
}
else if(!nxt[x.num][1])
{
nxt[x.num][1]=y.num;
dis[x.num][1]=abs(x.h-y.h);
}
return;
}
void query(int s,int x,long long &dista,long long &distb)
{
for(int i=20;i>=0;i--)
if(f[s][i][0]+f[s][i][1]<=x&&g[s][i])//从大到小,能塞就塞
{
dista+=f[s][i][0];
distb+=f[s][i][1];
x-=f[s][i][1]+f[s][i][0];
s=g[s][i];
}
if(nxt[s][1]&&dis[s][1]<=x)
dista+=dis[s][1];
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&city[i].h);
city[i].num=i;
}
for(int i=n;i>=1;i--)
{
S.insert(city[i]);
it=S.find(city[i]);
if(it!=S.begin())
{
it--;
update(city[i],*it);
if(it!=S.begin())
{
it--;
update(city[i],*it);
it++;
}
it++;
}
if((++it)!=S.end())
{
update(city[i],*it);
if((++it)!=S.end())
{
update(city[i],*it);
it--;
}
it--;
}
}
for(int i=1;i<=n;i++)//初始化
{
g[i][0]=nxt[nxt[i][1]][0];//从i开始两人轮流开一轮到的地方
f[i][0][0]=dis[i][1];//开一次b走的路径
f[i][0][1]=dis[nxt[i][1]][0];//开一次a走的路径
}
for(int j=1;j<=20;j++)
for(int i=1;i<=n;i++)
{
g[i][j]=g[g[i][j-1]][j-1];//从第一近到第二近
f[i][j][0]=f[i][j-1][0]+f[g[i][j-1]][j-1][0];//A走一轮,再走一轮
f[i][j][1]=f[i][j-1][1]+f[g[i][j-1]][j-1][1];//B走一轮,再走一轮
}
scanf("%d",&x0);
int s0=0;
long long a=1e15,b=0;
for(int i=1;i<=n;i++)
{
long long dista=0,distb=0;
query(i,x0,dista,distb);
if(distb&&(!s0||(dista*b<distb*a)))
{
s0=i;
a=dista;
b=distb;
}
}
printf("%d\n",s0);
scanf("%d",&m);
while(m--)
{
long long dista=0,distb=0;
int s,x;
scanf("%d%d",&s,&x);
query(s,x,dista,distb);
printf("%d %d\n",dista,distb);
}
return 0;
}