洛谷 P1086 开车旅行 【倍增+STL】

时间:2022-12-16 18:31:09

题目:

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走一轮,再走一轮

 

下面是参考代码:

 

 

洛谷  P1086     开车旅行     【倍增+STL】洛谷  P1086     开车旅行     【倍增+STL】
#include <iostream>
#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;
}
View Code