牛客多校第二场G transform(二分答案)

牛客多校第二场G transform(二分答案)

 

输入

2 3

1 2

2 3

输出

4

 

题目大意:在一个数轴上有n个集装箱,第 i 个集装箱的位置为x[i],且在集装箱内装有a[i]件货物,现在将这些集装箱内的货物进行移动(将一件货物从第 i 个集装箱移动到第 j 个集装箱的花费就为2*abs(x[i]-x[j]) ),求在总花费不超过T的情况下,最多能将多少货物移动到同一个集装箱内。

 

题目思路:

我们二分一个答案ans*,意为在花费不超过T的情况下ans*件货物可以被移动到同一个位置。很显然,这ans*件货物应该位于一个连续区间内(这不难弄明白)。

对于这个二分的答案ans*,我们枚举这个连续区间的左端点,往右边数ans*件货物达到的位置就是右端点。再确定中位点,也就是货物要运送到的位置。  然后判断这个区间移动的花费是否小于T。  

 

求区间花费的话我们可以用两个前缀和和两个后缀和来求,

prew[i]表示前 i 个集装箱的总货物,prex[i]表示将前 i 个集装箱的货物移动到第 i 个集装箱的花费,那么可以得到如下两个式子

prew[i]=prew[i-1]+a[i]

prex[i]=2*prew[i-1]*(x[i]-x[i-1])+prex[i-1]

那么将 l 到 r 这个区间内所有的货物移动到 r 这个集装箱的花费就为

prex[r]-prex[l-1]-2*prew[l-1]*(x[r]-x[l-1])

 

sufw[i]表示第 i 个到第 n 个集装箱的总货物,sufx[i]表示将第 i 个到第 n 个集装箱所有的货物移动到第 i 个集装箱的花费,那么可以得到如下两个式子

sufw[i]=sufw[i+1]+a[i]

sufx[i]=sufw[i+1]*2*(x[i+1]-x[i])+sufx[i+1]

那么将 l 到 r 这个区间内所有的货物移动到 l 这个集装箱的花费就为

sufx[l]-sufx[r+1]-sufw[r+1]*2*(x[r+1]-x[l])

 

区间花费=中位点左边的花费+中位点右边的花费,使用上面的两个公式即可。

 

 

 

#include<bits/stdc++.h>

using namespace std;

const int maxn=500010;

int n;

typedef long long ll;

ll t;

struct node

{

    int v,x;

    bool operator <(const node&aa)const

    {

        return aa.x>x;

    }

} a[maxn];

ll prevv[maxn];//prevv[i]表示从1~i有多少个货物,

ll prex[maxn];//prex[i]表示把1~i的货物全部搬到i的花费

ll sufv[maxn];//sufv[i]表示从i~n有多少个货物

ll sufx[maxn];//sufx[i]表示把i~n的货物全部搬到n的花费

ll culp(int l,int r)

{

    return prex[r]-prex[l-1]-(prevv[l-1]*(a[r].x-a[l-1].x));

}

ll culs(int l,int r)

{

    return sufx[l]-sufx[r+1]-(sufv[r+1]*(a[r+1].x-a[l].x));

}

int check(ll num)//枚举的答案

{

    ll num1=num/2+1;

    int l=1,r=2,mid=2;//这里的l和r是位置

    while(1)

    {

        while(r<=n&&prevv[r]-prevv[l-1]<num)//还没有满足

            r++;

        while(mid<=n&&prevv[mid]-prevv[l-1]<num1)

            mid++;

        if(r>n||mid>n) break;

        ll ans=culp(l,mid)+culs(mid,r-1)+(num-(prevv[r-1]-prevv[l-1]))*(a[r].x-a[mid].x);

        if(ans<=t)

            return 1;

        l++;

    }

    l=n-1;

    r=n;

    mid=n;

    while(1)

    {

        while(l>=1&&prevv[r]-prevv[l-1]<num)

            l--;

        while(mid>=2&&prevv[mid]-prevv[l-1]<num1)

            mid--;

        if(l<1||mid<2) break;

        ll ans=culp(l+1,mid)+culs(mid,r)+(num-(prevv[r]-prevv[l]))*(a[mid].x-a[l].x);

        if(ans<=t)

            return 1;

        r--;

    }

    return 0;

}

 

int main()

{

    memset(prevv,0,sizeof(prevv));

    memset(prex,0,sizeof(prex));

    memset(sufv,0,sizeof(sufv));

    memset(sufx,0,sizeof(sufx));

    scanf("%d%lld",&n,&t);

    ll l=1,r=0,mid;

    t/=2;

    for(int i=1; i<=n; i++)

    {

        scanf("%d",&a[i].x);//距离

    }

    for(int i=1; i<=n; i++)

        scanf("%d",&a[i].v);//每个点的物品数量

    sort(a+1,a+n+1);//按照位置前后来排序;

    for(int i=1; i<=n; i++)

    {

        prevv[i]=prevv[i-1]+a[i].v;

        prex[i]=prevv[i-1]*abs(a[i].x-a[i-1].x)+prex[i-1];

        r+=a[i].v;

    }

    for(int i=n; i>=1; i--)

    {

        sufv[i]=sufv[i+1]+a[i].v;

        sufx[i]=sufv[i+1]*abs(a[i].x-a[i+1].x)+sufx[i+1];

    }

    while(l<=r)//这里枚举的左右端点和中间点都是能搬运的货物的数量

    {

        mid=(l+r)/2;

        if(check(mid)) l=mid+1;

        else r=mid-1;

    }

    printf("%lld\n",r);

    return 0;

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值