zoukankan      html  css  js  c++  java
  • P1081 开车旅行

    传送门

    用倍增的思想

    设 A[ i ] 表示A在 i 位置走一步到达的城市以及经过的路程(这里我用结构体存A[ i ]),B同理

    设 f [ i ] [ j ] 表示从 i 位置出发,走 $2^j$ 轮后到达的城市(一轮即AB各走一次)

    dis[ i ] [ j ] 表示从 i 位置出发,走 $2^j$ 轮后经过总路程

    da [ i ] [ j ] 表示从 i 位置出发,走 $2^j$ 轮后A经过路程,db同理

    然后就可以愉快地xiagao

    但是现在有一个问题,怎么预处理出 A[ i ],B[ i ]

    把城市编号从大到小加入 set,每次加入一个城市前先求出 set 内离他最近的城市和第二近的城市

    如果按高度排序后此高度排名为 i ,那么最近的城市就在 i+1 和 i-1 中,次近的城市就在 i+1,i+2,i-1,i-2 中

    这个可以用 lower_bound 求出

    然后就可以搞了

    思维难度不大,具体实现起来一堆细节恶心得一批

    别忘了可能AB一人走一步走不了,但是A单独可以多走一步的情况

    关于题目的第一个问题,如果B的路程为0,那么就算A也为0 比值仍为INF

    一定仔细看题目啊,很多细节啊

    下面附上我那压行严重的巨丑代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<set>
    using namespace std;
    typedef long long ll;//有些变量该开long long 果断开
    inline int read()
    {
        int x=0,f=1; char ch=getchar();
        while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
        while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return x*f;
    }
    const int N=1e5+7;
    const ll INF=5e9+7;
    int n,m,x0;
    struct node
    {
        ll h; int id;//高度,以及编号
        node () { h=0; id=0; }
        inline bool operator < (const node &tmp) const {
            return h<tmp.h;
        }
    }A[N],B[N],d[N];//在A[i]B[i]中则是距离和编号
    multiset <node> s;
    multiset <node>::iterator it;
    int f[N][22];
    ll dis[N][22],da[N][22],db[N][22];
    void pre()
    {
        node GG;
        GG.h=INF; s.insert(GG); s.insert(GG); s.insert(GG); s.insert(GG);
        GG.h=-INF; s.insert(GG); s.insert(GG); s.insert(GG); s.insert(GG);//防止指针越界
        node mx,mxx;/*mx存最近,mxx存次近*/ ll t=0,aa=0,bb=0;
        for(int i=n;i;i--)
        {
            it=s.lower_bound(d[i]);
    
            aa=(*it).h; mx.h=aa-d[i].h; mx.id=(*it).id; it--;
            bb=(*it).h; mxx.h=d[i].h-bb; mxx.id=(*it).id;
            if(mxx.h<mx.h||(mxx.h==mx.h&&aa>bb)) swap(mx,mxx);//注意如果相等也要判一下
            if(!mx.id) mx.h=0;/*如果超出边界了路程统一为0*/ B[i]=mx;
    
            it--; t=d[i].h-(*it).h; if(t<mxx.h || (mxx.h==t&&(*it).h<d[mxx.id].h) ) mxx.h=t,mxx.id=(*it).id;//记得相等也要判一下
            it++; it++; it++; t=(*it).h-d[i].h; if(t<mxx.h || (mxx.h==t&&(*it).h<d[mxx.id].h) ) mxx.h=t,mxx.id=(*it).id;//记得相等也要判一下
            if(!mxx.id) mxx.h=0;/*同样判一下是否超出边界*/ A[i]=mxx;
    
            s.insert(d[i]);//别忘了加入d[i]
        }
        for(int i=1;i<=n;i++)//预处理倍增数组
        {
            f[i][0]=B[A[i].id].id;//计算f[i][0]
            if(f[i][0])//如果没出边界就正常处理
            {
                da[i][0]=A[i].h; db[i][0]=B[A[i].id].h;
                dis[i][0]=da[i][0]+db[i][0];
            }
            else da[i][0]=db[i][0]=dis[i][0]=INF;//不然设成INF
        }
        for(int k=1;k<=20;k++)
            for(int i=1;i<=n;i++)
            {
                f[i][k]=f[f[i][k-1]][k-1];
                if(f[i][k])//同样要判是否出边界
                {
                    da[i][k]=da[i][k-1]+da[f[i][k-1]][k-1];
                    db[i][k]=db[i][k-1]+db[f[i][k-1]][k-1];
                    dis[i][k]=dis[i][k-1]+dis[f[i][k-1]][k-1];
                }
                else da[i][k]=db[i][k]=dis[i][k]=INF;
            }
    }
    int ansa,ansb;//存AB的路程
    inline void slove(int pos,int x)//给定出发点和最大总路程求AB两人的路程
    {
        ansa=ansb=0;//初始为0
        for(int i=20;i>=0;i--)//倍增求路程
        {
            if(x-dis[pos][i]<0) continue;
            ansa+=da[pos][i]; ansb+=db[pos][i];//更新ansa,ansb
            x-=dis[pos][i]; pos=f[pos][i];//别忘了更新x和pos
        }
        if(x>=A[pos].h) ansa+=A[pos].h;//最后一定要特判A单独多走一步
    }
    void slove_problemA()//处理第一个问题
    {
        x0=read();
        int pos=0; double ans=INF,res=0;
        for(int i=1;i<=n;i++)
        {
            slove(i,x0);
            if(!ansb) res=INF;//判一下B路程为0
            else res=(double)ansa/(double)ansb;//ansa和ansb一定要先转double!!因为这个被坑了一天
            if( res<ans || (res==ans&&d[i].h>d[pos].h) )//注意如果相等也要判一下
                ans=res,pos=i;
        }
        printf("%d
    ",pos);
    }
    void slove_problemB()//处理第二个问题
    {
        m=read(); int s=0,x=0;
        while(m--)
        {
            s=read(); x=read();
            slove(s,x);
            printf("%d %d
    ",ansa,ansb);
        }
    }
    int main()
    {
        n=read();
        for(int i=1;i<=n;i++) d[i].h=read(),d[i].id=i;//d[i].id初始为i
        pre();
        slove_problemA();
        slove_problemB();
        return 0;
    }
  • 相关阅读:
    126
    125
    124
    123
    122
    121
    120
    119
    洛谷 P5407 【[THUPC2019]历史行程】
    济南清北学堂七日游
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/9860441.html
Copyright © 2011-2022 走看看