Description
坐标轴上有 n n 个集装箱,位置在,其中的货物有 ai a i 个,把一个货物从 u u 集装箱移动到集装箱的代价是 2⋅|xu−xv| 2 ⋅ | x u − x v | ,问在所用代价不超过 T T 的前提下,最多能把多少货物移动到一个集装箱
Input
第一行两个整数,之后输入 n n 个整数,最后输入 n n 个整数
(1≤n≤5⋅105,1≤T≤1018,0≤xi≤109,0≤ai≤104) ( 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 个货物时让代价最小值的该区间
假设当然区间为,且第 l l 个集装箱中有个货物没有集中,第 r r 个集装箱中有个货物要集中,那么每次当集中点从 i−1 i − 1 移动到 i i 时,可以很快维护代价,之后考虑该区间是否能够移动,显然只有当,也即左边货物距集中点距离大于右边货物距集中点距离时,可以把左边的货物移动到右边去,只要满足条件则一直右移并更新代价,只要代价不超过限制说明二分值合理,时间复杂度 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;
}