牛客网暑期多校训练第二场G题

链接:https://www.nowcoder.com/acm/contest/140/G
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

官方做法:因为我们要让货物移动总距离尽可能小,所以最后所使用的集装箱的初始位置在数轴上一定是一段区间。 如果固定了这个区间,那么最优方案就是把这些集装箱移动到这些集装箱的坐标中位数的位置。
答案满足可二分性,先二分答案。然后我们按照从左至右的顺序枚举区间的左端点,那么区间的右端点和 区间的中位数都是单调递增的,一遍枚举一遍维护即可。 复杂度O(n*log(sum(a[i])))

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<queue>
#include<map>
using namespace std;
typedef long long ll;
const int maxn=5e5+7;
const int INF=0x3f3f3f3f;
int n;
ll t;
ll x[maxn];
ll a[maxn];
ll sum1[maxn];
ll sum2[maxn];
ll s1(int x,int y){return sum1[max(y,x)]-sum1[min(y,x)-1];}
ll s2(int x,int y){return sum2[max(y,x)]-sum2[min(y,x)-1];}
bool check(ll k)//检查能否取到k
{
    int l=1,r=1,m=1;//左端点,右端点,中点
    //从左到右扫一遍
    while(l<=n&&r<=n&&m<=n)
    {
        while(r<n&&s1(l,r)<k)r++;
        while(m<n&&2*s1(l,m)<k)m++;
        if(s1(l,r)>=k&&x[m]*s1(l,m)-s2(l,m)+s2(r,m)-x[m]*s1(r,m)-(s1(r,l)-k)*(x[r]-x[m])<=t)return 1;
    //总的个数大于k   移动l-m的消耗        移动m-r的消耗           减去加上a[r]比k多的个数的消耗    总的消耗比t小
        l++;
    }
    l=r=m=n;
    //从右到左扫一遍
    while(l>=1&&r>=1&&m>=1)
    {
        while(l>1&&s1(l,r)<k)l--;
        while(m>1&&2*s1(m,r)<k)m--;
        if(s1(l,r)>=k&&x[m]*s1(l,m)-s2(l,m)+s2(r,m)-x[m]*s1(r,m)-(s1(r,l)-k)*(x[m]-x[l])<=t)return 1;
        r--;
    }
    return false;
}
int main()
{
    scanf("%d%lld",&n,&t);
    t/=2;
    for(int i=1;i<=n;++i)
        scanf("%lld",&x[i]);
    for(int i=1;i<=n;++i)
        scanf("%lld",&a[i]);
    for(int i=1;i<=n;++i)
       {
           sum1[i]=sum1[i-1]+a[i];
           sum2[i]=sum2[i-1]+a[i]*x[i];
       }
    ll l=1;
    ll r=sum1[n];
    ll ans;
    while(l<=r)
    {
        ll mid=(l+r)>>1;
        if(check(mid))
        {
            l=mid+1;
            ans=mid;
        }
        else r=mid-1;
    }
    printf("%lld\n",ans);
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值