• P1081 开车旅行


    题意:

    小 A 和小 B 决定利用假期外出旅行,他们将想去的城市从 11 到 NN 编号,且编号较小的城市在编号较大的城市的西边,

    已知各个城市的海拔高度互不相同,记城市 i 的海拔高度为 $H_i$​ ,城市 i 和城市 j 之间的距离 $d_{[i,j]}=|H_i-H_j|$ 。

    旅行过程中,小 A 和小 B 轮流开车,第一天小 A 开车,之后每天轮换一次。

    他们计划选择一个城市 SS 作为起点,一直向东行驶,并且最多行驶 X 公里就结束旅行。

    小 A 和小 B 的驾驶风格不同,小 B 总是沿着前进方向选择一个最近的城市作为目的地,而小 A 总是沿着前进方向选择第二近的城市作为目的地

    (注意:本题中如果当前城市到两个城市的距离相同,则认为离海拔低的那个城市更近)。

    如果其中任何一人无法按照自己的原则选择目的城市,或者到达目的地会使行驶的总距离超出 X 公里,他们就会结束旅行。

    在启程之前,小 A 想知道两个问题:

    1:对于一个给定的 $X=X_0$​ ,从哪一个城市出发,小 A 开车行驶的路程总数与小 B 行驶的路程总数的比值最小(如果小 B 的行驶路程为 0 ,此时的比值可视为无穷大,且两个无穷大视为相等)。

      如果从多个城市出发,小 A 开车行驶的路程总数与小 B 行驶的路程总数的比值都最小,则输出海拔最高的那个城市。

    2:对任意给定的 $X=X_i$​ 和出发城市 $S_i$​ ,小 A 开车行驶的路程总数以及小 B 行驶的路程总数。

    以f[i][j]记录从i点经过$2^j$轮后他们所到的点

    以disa[i][j]记录a从i点经过$2^j$天自己走的距离

    以disb[i][j]记录b从i点经过$2^j$天自己走的距离

    以ato[i]记录a从i点走一步所到的点

    以bto[i]记录b从i点走一步所到的点

    首先,把所有城市按海拔排序,并建立排序前后的映射

    从第一个城市开始枚举,那么所有的城市都在它的东边

    然后对应到排序好的城市中,处理出ato[i],bto[i]

    处理完把当前城市删除,第二个变成第一个,因此再次求的时候仍然所有点都在东边

    之后处理st表,模拟即可

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<cctype>
    #include<algorithm>
    using namespace std;
    #define int long long
    #define olinr return
    #define _ 0
    #define nmr 105050
    #define love_nmr 0
    #define DB double
    int n;
    struct node
    {
        int pre;
        int nxt;
        int h;
        int id;
        friend bool operator < (const node &a,const node &b)
        {
            return a.h<b.h;
        }
    }s[nmr];
    int disa[nmr][31];
    int disb[nmr][31];
    int f[nmr][31];
    int ato[nmr];
    int bto[nmr];
    int X;
    int m;
    int mp[nmr];
    int ans;
    DB minn=0x7fffffff;
    inline int read()
    {
        int x=0,f=1;
        char ch=getchar();
        while(!isdigit(ch))
        {
            if(ch=='-')
                f=-f;
            ch=getchar();
        }
        while(isdigit(ch))
        {
            x=(x<<1)+(x<<3)+(ch^48);
            ch=getchar();
        }
        return x*f;
    }
    inline void put(int x)
    {
        if(x<0)
        {
            x=-x;
            putchar('-');
        }
        if(x>9)
            put(x/10);
        putchar(x%10+'0');
    }
    inline int abss(int x)
    {
        if(x<0) return -x;
        return x;
    } 
    inline bool near_left(int now,int l,int r)
    {
        if(!l) return false;
        if(!r) return true;
        int disl=abss(s[l].h-s[now].h);
        int disr=abss(s[r].h-s[now].h);
        if(disl<disr) return true;
        if(disl>disr) return false;
        return s[l].h<s[r].h;
    }
    inline int opposite(int now,int l,int r)
    {
        if(!l) return s[r].id;
        if(!r) return s[l].id;
        int disl=abss(s[l].h-s[now].h);
        int disr=abss(s[r].h-s[now].h);
        if(disl<disr) return s[l].id;
        if(disl>disr) return s[r].id;
        if(s[l].h<s[r].h) return s[l].id;
        return s[r].id;
    }
    inline void beizeng()
    {
        for(int i=1;i<=n;i++)   //a---->b---->c
        {
            f[i][0]=bto[ato[i]];   //a---->c
            disa[i][0]=abss(s[mp[i]].h-s[mp[ato[i]]].h);   //a---->b
            disb[i][0]=abss(s[mp[f[i][0]]].h-s[mp[ato[i]]].h);  //b---->c
        }
        for(int j=1;j<=23;j++)
            for(int i=1;i<=n;i++)
            {
                f[i][j]=f[f[i][j-1]][j-1];
                disa[i][j]=disa[i][j-1]+disa[f[i][j-1]][j-1];
                disb[i][j]=disb[i][j-1]+disb[f[i][j-1]][j-1];
            }
    }
    inline void running(int sta,int &a,int &b,int limit)
    {
        for(int i=22;i>=0;i--)
        {
            if(f[sta][i]&&(a+b+disa[sta][i]+disb[sta][i])<=limit)  //模拟倍增过程,A、B一起跑
            {
                a+=disa[sta][i];
                b+=disb[sta][i];
                sta=f[sta][i];
            }
        }
        if(ato[sta]&&(a+b+disa[sta][0])<=limit) //现在AB同时跑不行,但可以看看A单独跑行不行!!
            a+=disa[sta][0];
    }
    signed main()
    {
        n=read();
        for(int i=1;i<=n;i++)
        {
            s[i].h=read();
            s[i].id=i;
        }
        sort(s+1,s+n+1);
        for(int i=1;i<=n;i++)
        {
            s[i].pre=i-1;
            s[i].nxt=i+1;
            mp[s[i].id]=i;       //排序前后映射
        }
        s[n].nxt=s[1].pre=0;      //排序后的左右
        for(int i=1;i<=n;i++)
        {
            int pos=mp[i];
            int l=s[pos].pre;      //此时l,r都在当前的东边!
            int r=s[pos].nxt;        
            if(near_left(pos,l,r))    //判断左边是否最近
            {
                bto[i]=s[l].id;
                ato[i]=opposite(pos,s[l].pre,r);   //若左边近,用左边的左边和右边比次近
            }
            else
            {
                bto[i]=s[r].id;
                ato[i]=opposite(pos,l,s[r].nxt);
            }
            if(r) s[r].pre=l;    //用完把当前删掉,保证下一个是最西边的
            if(l) s[l].nxt=r;
        }
        beizeng();   //倍增预处理
        X=read();
        m=read();
        for(int i=1;i<=n;i++)
        {
            int tota=0;
            int totb=0;
            running(i,tota,totb,X);
            if(totb&&(((DB)tota/(DB)totb)<minn))
            {
                minn=(DB)tota/(DB)totb;
                ans=i;
            }
        }
        if(ans==0) ans=s[n].id;
        put(ans);
        putchar('
    ');
        for(int i=1;i<=m;i++)
        {
            int sta,limit;
            sta=read();
            limit=read();
            int tota=0;
            int totb=0;
            running(sta,tota,totb,limit);
            put(tota);
            putchar(' ');
            put(totb);
            putchar('
    ');
        }
        olinr ~~(0^_^0)+love_nmr;
    }
  • 相关阅读:
    简历
    Servlet 三大域对象
    jsp分页
    fastadmin开发api的时候,遇到一些疑问
    RSA的基本原理,B站视频2部
    php函数注释规范
    vscode查看函数跳转定义处,和跳转引用处
    api接口发的规范和具体的细节
    restful规范是什么?为什么推荐使用restful风格,他的好处是什么?
    在线考试系统的逻辑思路
  • 原文地址:https://www.cnblogs.com/olinr/p/9535528.html
Copyright © 2020-2023  润新知