二分+双指针扫描
首先那两个重要结论是不难发现的。
接着我们就会考虑枚举每一个放箱子的位置,然后二分范围,再二分具体到哪一个箱子位置。这就是算法二,应该是没什么办法优化,注定要TLE的。。。
我们考虑把用一定时间取尽量多的箱子变为取一定的箱子用最少的时间,这样可以二分答案了。注意到一个重要性质,如果确定要取箱子的数量,那么取箱子的x坐标区间是随s从1到n而单调的,于是套上双指针扫描即可- -
计算的时候比较麻烦,把式子推好再写进去,还好写的时候没出现什么巨大的偏差
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 500005
#define ll long long
using namespace std;
int n;
ll t, pre1[N], pre2[N], sum[N], x[N], a[N], siz[N];
ll countt(int s, int l, int r)
{
ll ret=0;
ret+=pre1[r]-pre1[s]-(sum[r]-sum[s])*x[s];
ret+=pre2[l]-pre2[s]-(sum[s-1]-sum[l-1])*(x[n]-x[s]);
ret-=(a[r]-siz[r])*(x[r]-x[s]);
ret-=(a[l]-siz[l])*(x[s]-x[l]);
ret*=2;
return ret;
}
bool check(ll lim)
{
memset(siz,0,sizeof(siz));
int l = 1, r = 0;
ll tmp = lim;
while(tmp)
{
++r;
ll k = min(a[r],tmp);
siz[r]=k;
tmp-=k;
}
if(countt(1,l,r)<=t)return 1;
for(int i = 2; i <= n; i++)
{
while(x[r]-x[i]<x[i]-x[l])
{
if(siz[r]==a[r])
{
if(r<n)
{
++r;
continue;
}
else
{
break;
}
}
ll tmp=min(a[r]-siz[r],siz[l]);
siz[r]+=tmp;
siz[l]-=tmp;
if(!siz[l])l++;
}
if(countt(i,l,r)<=t)return 1;
}
return 0;
}
int main()
{
scanf("%d%lld",&n,&t);
for(int i = 1; i <= n; i++)
scanf("%lld",&x[i]);
for(int i = 1; i <= n; i++)
scanf("%lld",&a[i]);
for(int i = 1; i <= n; i++)
{
sum[i]=sum[i-1]+a[i];
pre1[i]=pre1[i-1]+x[i]*a[i];
int j = n-i+1;
pre2[j]=pre2[j+1]+(x[n]-x[j])*a[j];
}
ll l=0, r=sum[n];
while(l<r)
{
ll mid=(l+r+1)>>1;
if(check(mid))l=mid;
else r=mid-1;
}
printf("%lld\n",l);
}