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

78 篇文章 0 订阅
7 篇文章 0 订阅

题目描述

这里写图片描述
这里写图片描述

题解

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

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

代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
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 <hp> 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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值