洛谷 P1086 开车旅行 【倍增+STL】
题目:
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> #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; }