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

 

 

 

 

 

 

 


 

 

 

 

 

posted @ 2017-07-19 21:27  Captain_fcj  阅读(194)  评论(0编辑  收藏  举报