题目:在一个数轴上有n个集装箱,第 i 个集装箱的位置为x[i],且在集装箱内装有w[i]件货物,现在将这些集装箱内的货物进行移动(将一件货物从第 i 个集装箱移动到第 j 个集装箱的花费就为2*abs(x[i]-x[j]) ),求在总花费不超过T的情况下,最多能将多少货物移动到同一个集装箱内。
思路:二分一下num个集装箱被移到一起,用尺取判断是否存在可行方案。判断时由num可以确定区间的长度,先按左端点这别的货物都移过去最优时判断,再按右端点优先。因为符合贪心性质,所以肯定会有一个最优的。当选定一个区间时,最终货物集合的地点肯定是中位数(num/2+1)这个位置,注意不是中间位置。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=5e5+10;
struct node{
ll x,w;
}a[maxn];
bool cmp(const node &a,const node &b)
{
return a.x<b.x;
}
ll prew[maxn],pres[maxn],sufw[maxn],sufs[maxn];
int n;
ll m;
ll getsum1(int l,int r)
{
ll ans=pres[r]-pres[l];
ans-=2*prew[l-1]*(a[r].x-a[l].x);
return ans;
}
ll getsum2(int l,int r)
{
ll ans=sufs[l]-sufs[r];
ans-=2*sufw[r+1]*(a[r].x-a[l].x);
return ans;
}
int check(int num)
{
int l=1,r=1,mid=1;
while(r<=n)
{
while(r<=n&&prew[r]-prew[l-1]<num) r++;
while(mid<=n&&prew[mid]-prew[l-1]<num/2+1) mid++;
if(r>n||mid>n) break;
ll sum=getsum1(l,mid);
sum+=getsum2(mid,r-1);
sum+=2*(num-(prew[r-1]-prew[l-1]))*(a[r].x-a[mid].x);
if(sum<=m) return true;
l++;
}
l=n,r=n,mid=n;
while(l>0)
{
while(l>0&&sufw[l]-sufw[r+1]<num) l--;
while(mid>0&&sufw[mid]-sufw[r+1]<num/2+1) mid--;
if(l<1||mid<1) break;
ll sum=getsum1(l+1,mid);
sum+=getsum2(mid,r);
sum+=2*(num-(sufw[l+1]-sufw[r+1]))*(a[mid].x-a[l].x);
if(sum<=m) return true;
r--;
}
return false;
}
int main()
{
scanf("%d%lld",&n,&m);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i].x);
ll S=0;
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i].w);
S+=a[i].w;
}
sort(a+1,a+n+1,cmp);
memset(sufw,0,sizeof sufw);
memset(sufs,0,sizeof sufs);
memset(prew,0,sizeof prew);
memset(pres,0,sizeof pres);
a[0]=a[1];a[n+1]=a[n];
for(int i=1;i<=n;i++)
{
prew[i]=prew[i-1]+a[i].w;
pres[i]=pres[i-1]+2ll*prew[i-1]*(a[i].x-a[i-1].x);
}
for(int i=n;i>=1;i--)
{
sufw[i]=sufw[i+1]+a[i].w;
sufs[i]=sufs[i+1]+2ll*sufw[i+1]*(a[i+1].x-a[i].x);
}
ll l=1,r=S,ans=0;
while(l<=r)
{
ll mid=(l+r)>>1;
if(check(mid))
{
ans=mid;
l=mid+1;
}
else
r=mid-1;
}
printf("%lld\n",ans);
return 0;
}