比赛链接:
@[toc]

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

codeJan 非常喜欢旅行。现在有 n 个城市排在一条线上,并且 codeJan 的位置不和任何一个城市的位置重叠。 codeJan
想要游览 m
个城市,同时因为时间是不断变化的,游览一个城市多次也是允许的,但是不能永远待在一个城市,否则那样太无聊了。给出这些城市的位置,codeJan
想要知道游览 m 个城市至少需要走多少米? 输入描述: 第一行是一个T≤20代表测试组数。
每组第一行是三个正整数n,m,p,分别代表城市数量、codeJan想要浏览的城市数量和codeJan当前的位置(单位为米)。
第二行包含n个正整数pos[i]表示第i个城市的位置,单位为米。 输入保证pos[i]<posi+1,并且p ≠
posi。 输出描述: 对于每组输入数据输出一个正整数表示 codeJan 至少需要走的距离。

示例1
输入

3 
2 2 2 
1 3 
2 2 1 
2 3 
4 3 4 
1 3 5 6

输出

3
2
3

说明

对于第一个样例的坐标最优移动顺序可以是:2→3→1,移动距离一共是3。
对于第二个样例的坐标最优移动顺序可以是:1→2→3,移动距离一共是2。
对于第三个样例的坐标最优移动顺序可以是:4→5→6→5,移动距离一共是3。

备注:
2≤n≤10^5^,1≤m≤10^5^ ,1≤p,pos[i]≤10^9^。

题解:

因为一个城市可以走多次,但不允许停留,那么最佳方案就是从p到两个距离最小的城市,然后反复移动。
这样只用考虑在哪两个点之间徘徊,然后让p前往,答案取最小就好
abs(pos [ai-] - pos [ai]) * sum
sum=剩下的所需要的浏览的城市数量(因为从p到这两个城市的过程中也有经过其他城市)
但问题是我们保证率ai与ai-1这两个城市的距离最小,但是p和这两个城市以及其他城市的距离大小我们无法保证,如果p旁边就有个很近的城市,距离小于ai与ai-1之间的距离,那不浏览这个城市岂不血亏
就比如
数据:
1
3 10 5
4 13 17
按照我们的方法从p=5到13,然后13到17,然后循环,这两个城市距离是4,但从p到4再回来只需要2步,从p到13这段路是必须走的嘛,就先当于浏览相同的城市却少走了2步,所以你会发现最佳应该是先到4,再到13,然后徘徊。如果第一个城市不是4是而是2,从p到1再回来是6步,大于4步,那我们就不要管4,直接前往13,13到17徘徊就可以了。
那会不会从p到附近两个点或者多个,更会优化,这样是不会的,因为我们求出来ai与ai-1的距离是最小的,也就是除此之外的两个点一定比他俩距离大,那就不必要了,因为p的起始点不是城市,所以才会存在可能要从p先到附近更近的点。
总结下
我们先看看p附近有没有更近城市,然后再去城市ai与ai-1

我大致就是理解到这个程度
此处借用大佬的代码,我在里面写了详细的注释,结合代码看看分析
具体证明可以看这个题解大佬题解

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=1e5+1;
int pos[maxn];int n,m,p,x;
signed main(){
    int T;
    cin>>T;
    while(T--){
        x=0;
        cin>>n>>m>>p;
        for(int i=1;i<=n;++i)
        {
            scanf("%lld",&pos[i]);
            if(p>pos[i])
            {
                x=i;//取p的左侧最近的城市 
            }
        }
        int ans=1e17;
        for(int i=1;i<n;++i)
        {//i-i+1徘徊
            if(i<=x)//如果当前城市位于p的左侧 
            {
                if(x-i+1>m)//p与当前城市i之间的城市数量大于m 
                {
                    continue;//如果去城市i的话会造成浪费,所以筛掉 
                }
                int sy=m-(x-i+1);//因为从p到i会经过x-i+1个城市,还剩下m-(x-i+1)个城市 
                int dis=p-pos[i];//当前城市与p点的距离 
                ans=min(ans,dis+sy*(pos[i+1]-pos[i]));//从p直接前往城市i,并在i与i+1之间循环sy次,将剩下sy个城市浏览完 
                if(sy&&x+1<=n) 
                {
                    ans=min(ans,(pos[x+1]-p)*2+dis+(sy-1)*(pos[i+1]-pos[i]));//去p的右边看看,有没有符合条件的点,使得距离还能缩短 
                }
            }
            else//如果位于p的右侧 
            {//下面的操作和上面是一样的,正好反过来 
                if(i-x>m)continue;
                int sy=m-(i-x),dis=pos[i]-p;

                ans=min(ans,dis+sy*(pos[i+1]-pos[i]));

                if(sy&&x>=1)
                {
                    ans=min(ans,(p-pos[x])*2+dis+(sy-1)*(pos[i+1]-pos[i]));
                }
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}