Newcoder 140 G.transform(二分)

本文介绍了一种算法,用于解决在给定成本限制下,如何将坐标轴上的集装箱内的货物最优地集中到一个点的问题。通过二分搜索和动态维护代价最小的区间,实现了在不超过总成本T的前提下,最大化集中到一个点的货物数量。算法的时间复杂度为O(nlog(Σai))。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

坐标轴上有nn个集装箱,位置在xi,其中的货物有aiai个,把一个货物从uu集装箱移动到v集装箱的代价是2|xuxv|2⋅|xu−xv|,问在所用代价不超过TT的前提下,最多能把多少货物移动到一个集装箱

Input

第一行两个整数n,T,之后输入nn个整数xi,最后输入nn个整数ai

(1n5105,1T1018,0xi109,0ai104)(1≤n≤5⋅105,1≤T≤1018,0≤xi≤109,0≤ai≤104)

Output

输出在所用代价不超过TT的前提下,最多能把多少货物移动到一个集装箱

Sample Input

2 3
1 2
2 3

Sample Output

4

Solution

二分货物数量k,显然最后集中的货物是一段连续的区间,且当集中点右移时区间端点移动也是单调的,故从左往右维护每点作为集中点、集中不超过kk个货物时让代价最小值的该区间

假设当然区间为[l,r],且第ll个集装箱中有numl个货物没有集中,第rr个集装箱中有numr个货物要集中,那么每次当集中点从i1i−1移动到ii时,可以很快维护代价,之后考虑该区间是否能够移动,显然只有当xixl>xrxi,也即左边货物距集中点距离大于右边货物距集中点距离时,可以把左边的货物移动到右边去,只要满足条件则一直右移并更新代价,只要代价不超过限制说明二分值合理,时间复杂度O(nlog(ai))O(nlog(∑ai))

Code

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
#define maxn 500005
int n,x[maxn],a[maxn];
ll T,sum[maxn];
ll count(int l,int r,int numl,int numr)
{
    if(l==r)return numr-numl;
    return sum[r-1]-sum[l]+a[l]-numl+numr;
}
bool check(ll k)
{
    int l=1,r=n+1,numl=0,numr=0;
    ll cost=0,num=0;
    for(int i=1;i<=n;i++)
        if(num+a[i]<=k)
        {
            num+=a[i];
            cost+=(ll)(x[i]-x[1])*a[i];
        }
        else
        {
            r=i;
            numr=k-num;
            cost+=(ll)(x[i]-x[1])*numr;
            break;
        }
    if(cost<=T)return 1;
    for(int i=2;i<=n;i++)
    {
        cost+=(ll)(x[i]-x[i-1])*(count(l,i,numl,0)-count(i,r,0,numr));
        while(r<=n&&x[i]-x[l]>x[r]-x[i])
        {
            int num=min(a[l]-numl,a[r]-numr);
            cost+=(ll)((x[r]-x[i])-(x[i]-x[l]))*num;
            numl+=num;
            if(numl==a[l])l++,numl=0;
            numr+=num;
            if(numr==a[r])r++,numr=0;
        }
        if(cost<=T)return 1;
    }
    return 0;
} 
int main()
{
    while(~scanf("%d%lld",&n,&T))
    {
        T/=2;
        for(int i=1;i<=n;i++)scanf("%d",&x[i]);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i];
        ll l=1,r=sum[n],mid,ans;
        while(l<=r)
        {
            mid=(l+r)/2;
            if(check(mid))ans=mid,l=mid+1;
            else r=mid-1;
        }
        printf("%lld\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值