题目大意:
数轴上有n个集装箱,第i个集装箱位于坐标x[i],有a[i]件货物。现在要把集装箱进行一些移动,求在所有货物移动总距离不超过T的情况下,最多能把多少个集装箱移动到同一个位置
做法:
因为我们要让货物移动总距离尽可能小,所以最后所使用的集装箱的初始位置在数轴上一定是一段区间。如果固定了这个区间,那么最优方案就是把这些集装箱移动到这些集装箱的坐标中位数的位置。答案满足可二分性,先二分答案。然后我们按照从左至右的顺序枚举区间的中位数位置,那么区间的左端点和右端点都是单调递增的,一遍枚举一遍维护即可。
复杂度O(n*log(sum(a[i]))) (差不多参考了官方题解)
代码:
#include<bits/stdc++.h>
#define N 500005
#define P pair<int,int>
using namespace std;
typedef long long ll;
const int M=1e9+7;
const int inf=1e9+7;
int a[N],n,b[N];
ll x[N],pre[N],T;
bool check(ll need)
{
int l=1,r=2;
ll lsum=a[1],rsum=0,c=0;
memcpy(b,a,sizeof(a));
b[1]=0;
while(lsum+rsum<=need&&r<=n){
ll res=min((ll)a[r],need-lsum-rsum);
rsum+=res;
c+=(x[r]-x[1])*res;
b[r]-=res;
if(!b[r])r++;
if(lsum+rsum>=need)break;
}
if(c<=T)return 1;
for(int i=2;i<=n;i++){
c+=(lsum-rsum)*(x[i]-x[i-1]);
lsum+=a[i]-b[i];
rsum-=a[i]-b[i];
while(r<=n&&x[r]-x[i]<x[i]-x[l]){
ll tmp=min(b[r],a[l]-b[l]);
c+=tmp*(x[r]-x[i]-x[i]+x[l]);
b[l]+=tmp;lsum-=tmp;
b[r]-=tmp;rsum+=tmp;
if(b[l]==a[l])l++;
if(!b[r])r++;
}
if(c<=T)return 1;
}
return 0;
}
int main()
{
scanf("%d%lld",&n,&T);
T/=2;
for(int i=1;i<=n;i++)
scanf("%lld",&x[i]);
ll l=0,r=pre[n];
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
r+=a[i];
}
while(l<=r)
{
ll m=l+r>>1;
if(check(m))l=m+1;
else r=m-1;
}
printf("%lld\n",r);
return 0;
}
/*
3 26
1 3 6
5 5 5
*/