Newcoder 140 G.transform(二分)

Description

坐标轴上有 n n 个集装箱,位置在xi,其中的货物有 ai a i 个,把一个货物从 u u 集装箱移动到v集装箱的代价是 2|xuxv| 2 ⋅ | x u − x v | ,问在所用代价不超过 T T 的前提下,最多能把多少货物移动到一个集装箱

Input

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

(1n5105,1T1018,0xi109,0ai104) ( 1 ≤ n ≤ 5 ⋅ 10 5 , 1 ≤ T ≤ 10 18 , 0 ≤ x i ≤ 10 9 , 0 ≤ a i ≤ 10 4 )

Output

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

Sample Input

2 3
1 2
2 3

Sample Output

4

Solution

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

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

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、付费专栏及课程。

余额充值