[BZOJbegin][NOIP十连测第五场]Travel(数学相关+乱搞)

题目描述

[BZOJbegin][NOIP十连测第五场]Travel(数学相关+乱搞)_第1张图片
[BZOJbegin][NOIP十连测第五场]Travel(数学相关+乱搞)_第2张图片

题解

无解的情况很好判断:当l=0,s!=1 或 l=n-1,s!=n是没有合法路径的。
可以把所有的点看成若干条线段,然后求线段的覆盖次数。
当起点为1,终点为n,需要向左跳L次,那么至少有L条线段是需要被覆盖3次的,其余都被覆盖一次。贪心地求前L小的线段使之被覆盖3次就能得到最优解。
推广到起点和终点不为1的情况。假设起点为s,终点为e,并且s < e,那么[s,e]这一段是和上面一种情况完全等价的。但是[1,s][e,n]这2段里的所有线段都是要被覆盖两遍的。也就是说,如果[1,s][e,n]里一共有x条线段的话,我们只需要从[s,e]里选出L-x条线段使之覆盖3遍就可以了。
e在s左边实际上是和s < e等价的,因为向左跳n-L-1次可以转化为向右跳L次。
也可以通过每一条遍的贡献考虑,[1,s][e,n]的边都贡献了向左跳一次,[s,e]之间被覆盖了三次的边也贡献了向左跳一次。

这题在BZ上交并不用输出方案。如果要是输出方案的话会变得及其麻烦。所以懒惰的Po没有写。。。

代码

#include
#include
#include
#include
using namespace std;
#define N 200005
#define LL long long
int n,L,s,l;
LL mid,bes,chosen,cost,ans;
LL x[N];
struct hp
{
    LL val;
    bool operator < (const hp &x) const
    {
        return val>x.val;
    }
};
priority_queue  q;
int main()
{
    freopen("travel.in","r",stdin);
    freopen("travel.out","w",stdout);
    scanf("%d%d%d",&n,&L,&s);
    for (int i=1;i<=n;++i) scanf("%I64d",&x[i]);
    if (L==0&&s!=1||L==n-1&&s!=n)
    {
        puts("-1");
        return 0;
    }
    if (L==0&&s==1|L==n-1&&s==n)
    {
        printf("%I64d\n",x[n]-x[1]);
        return 0;
    }
    l=L;mid=x[s+1]-x[s],bes=x[n]-x[1]-mid,chosen=0;cost=mid+bes*2;ans=cost;
    for (int e=s+2;e<=n;++e)
    {
        bes-=x[e]-x[e-1];
        mid+=x[e]-x[e-1];
        if (n-e+s-1>=l)
        {
            cost=mid+bes*2;
            ans=min(ans,cost);

            hp now;now.val=x[e]-x[e-1];
            q.push(now);
            continue;
        }
        hp top;top.val=0;
        if (!q.empty()) top=q.top(),q.pop();
        else continue;
        chosen+=top.val;
        cost=chosen*3+mid-chosen+bes*2;
        ans=min(ans,cost);

        hp now;now.val=x[e]-x[e-1];
        q.push(now);
    }

    l=n-L-1;mid=x[s]-x[s-1],bes=x[n]-x[1]-mid,chosen=0;cost=mid+bes*2;ans=min(ans,cost);
    for (int e=s-2;e>=1;--e)
    {
        bes-=x[e+1]-x[e];
        mid+=x[e+1]-x[e];
        if (e-1+n-s>=l)
        {
            cost=mid+bes*2;
            ans=min(ans,cost);

            hp now;now.val=x[e+1]-x[e];
            q.push(now);
            continue;
        }
        hp top;top.val=0;
        if (!q.empty()) top=q.top(),q.pop();
        else continue;
        chosen+=top.val;
        cost=chosen*3+mid-chosen+bes*2;
        ans=min(ans,cost);

        hp now;now.val=x[e+1]-x[e];
        q.push(now);
    }
    printf("%I64d\n",ans);
}

你可能感兴趣的:(题解,数学相关,乱搞)