1. 程式人生 > >洛谷 P1086 開車旅行 【倍增+STL】

洛谷 P1086 開車旅行 【倍增+STL】

begin open cnblogs sin display mat date tor 分析

題目:

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; }
View Code


洛谷 P1086 開車旅行 【倍增+STL】