牛客多校赛 G transform(二分)

本文探讨了一个关于货物移动的问题,目标是在总花费不超过限定值的情况下,最大化移动到同一位置的货物数量。通过二分查找和枚举策略,文章提供了一种有效的解决方案。

https://blog.youkuaiyun.com/Lee_w_j__/article/details/81157488

题目描述
White Cloud placed n containers in sequence on a axes. The i-th container is located at x[i] and there are a[i] number of products in it.
White Rabbit wants to buy some products. The products which are required to be sold must be placed in the same container.
The cost of moving a product from container u to container v is 2*abs(x[u]-x[v]).
White Cloud wants to know the maximum number of products it can sell. The total cost can't exceed T.

输入描述:
The first line of input contains 2 integers n and T(n <= 500000,T <= 1000000000000000000)
In the next line there are n increasing numbers in range [0,1000000000] denoting x[1..n]
In the next line there are n numbers in range[0,10000] denoting a[1..n]
输出描述:
Print an integer denoting the answer.
示例1

输入

2 3
1 2
2 3
输出
复制

4

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

题目思路:既然要使得花费在不超过T的情况尽可能多的移动货物,那么我们肯定是将一个区间内的所有货物移到坐标中位的集装箱上。那么我们就可以对答案进行二分,然后枚举所要移动的区间的左端点,再找到中位点和右端点,然后判断这个区间移动的花费是否小于T。

#include<iostream>
#include<bits/stdc++.h>
#include<stdio.h>
#include<cmath>
#include<string.h>
#define mod 1000000000
#define ll long long
using namespace std;
ll n,k;
struct code
{
    ll x,v;
} point[500005];
bool cmp(code a,code b)
{
    return a.x<b.x;
}
ll prenum[500005],prevv[500005],sufnum[500005],sufv[500005];
ll cal_pre(ll l,ll r)//l-r区间移动r位置的费用
{
    return prevv[r]-prevv[l-1]-prenum[l-1]*(point[r].x-point[l-1].x);
}
ll cal_suf(ll l,ll r)//l-r区间移动到l位置的费用
{
    return sufv[l]-sufv[r+1]-sufnum[r+1]*(point[r+1].x-point[l].x);
}
bool check(ll sum)
{
    ll sum2=(sum+1)/2;
    ll l=1,r=1,mid=1;
    while(1)//从前向后枚举区间
    {
        while(r<=n&&prenum[r]-prenum[l-1]<sum)//凑够数量
            r++;
        while(mid<=n&&prenum[mid]-prenum[l-1]<sum2)//找中间位置
            mid++;
        if(r>n||mid>n)
            break;
        ll tx=prenum[r]-prenum[l-1]-sum;//多余枚举答案的数量
        ll cost=cal_pre(l,mid)+cal_suf(mid,r)-tx*(point[r].x-point[mid].x);
        if(cost<=k)
            return true;
        l++;
    }
    l=n,r=n,mid=n;
    while(1)//从后向前枚举区间
    {

        while(l>=1&&sufnum[l]-sufnum[r+1]<sum)
            l--;
        while(mid>=1&&sufnum[mid]-sufnum[r+1]<sum2)
            mid--;
        if(l<1||mid<1)
            break;
        ll tx=sufnum[l]-sufnum[r+1]-sum;
        ll cost=cal_pre(l,mid)+cal_suf(mid,r)-tx*(point[mid].x-point[l].x);
        if(cost<=k)
            return true;
        r--;
    }
    //两个方向枚举是多余的从那边去掉
    return false;
}
int main()
{
    scanf("%lld%lld",&n,&k);
    k/=2;
    for(int i=1; i<=n; i++)
        scanf("%lld",&point[i].x);
    for(int i=1; i<=n; i++)
        scanf("%lld",&point[i].v);
    sort(point+1,point+n+1,cmp);
    prenum[0]=point[0].v;
    prevv[0]=0;
    for(int i=1; i<=n; i++)
    {
        prenum[i]=prenum[i-1]+point[i].v;
        prevv[i]=prenum[i-1]*(point[i].x-point[i-1].x)+prevv[i-1];
    }//前缀,i前面所有点移动到i的数量和花费
    sufnum[n+1]=0;
    sufv[n+1]=0;
    for(int i=n; i>0; i--)
    {
        sufnum[i]=sufnum[i+1]+point[i].v;
        sufv[i]=sufnum[i+1]*(point[i+1].x-point[i].x)+sufv[i+1];
    }//后缀,i后面所有点移动到i的数量和花费
    ll l=0,r=prenum[n];
    while(l<=r)
    {
        ll 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、付费专栏及课程。

余额充值