牛客多校第二场G transform(二分答案)
输入
2 3
1 2
2 3
输出
4
题目大意:在一个数轴上有n个集装箱,第 i 个集装箱的位置为x[i],且在集装箱内装有a[i]件货物,现在将这些集装箱内的货物进行移动(将一件货物从第 i 个集装箱移动到第 j 个集装箱的花费就为2*abs(x[i]-x[j]) ),求在总花费不超过T的情况下,最多能将多少货物移动到同一个集装箱内。
题目思路:
我们二分一个答案ans*,意为在花费不超过T的情况下ans*件货物可以被移动到同一个位置。很显然,这ans*件货物应该位于一个连续区间内(这不难弄明白)。
对于这个二分的答案ans*,我们枚举这个连续区间的左端点,往右边数ans*件货物达到的位置就是右端点。再确定中位点,也就是货物要运送到的位置。 然后判断这个区间移动的花费是否小于T。
求区间花费的话我们可以用两个前缀和和两个后缀和来求,
prew[i]表示前 i 个集装箱的总货物,prex[i]表示将前 i 个集装箱的货物移动到第 i 个集装箱的花费,那么可以得到如下两个式子
prew[i]=prew[i-1]+a[i]
prex[i]=2*prew[i-1]*(x[i]-x[i-1])+prex[i-1]
那么将 l 到 r 这个区间内所有的货物移动到 r 这个集装箱的花费就为
prex[r]-prex[l-1]-2*prew[l-1]*(x[r]-x[l-1])
sufw[i]表示第 i 个到第 n 个集装箱的总货物,sufx[i]表示将第 i 个到第 n 个集装箱所有的货物移动到第 i 个集装箱的花费,那么可以得到如下两个式子
sufw[i]=sufw[i+1]+a[i]
sufx[i]=sufw[i+1]*2*(x[i+1]-x[i])+sufx[i+1]
那么将 l 到 r 这个区间内所有的货物移动到 l 这个集装箱的花费就为
sufx[l]-sufx[r+1]-sufw[r+1]*2*(x[r+1]-x[l])
区间花费=中位点左边的花费+中位点右边的花费,使用上面的两个公式即可。
#include<bits/stdc++.h>
using namespace std;
const int maxn=500010;
int n;
typedef long long ll;
ll t;
struct node
{
int v,x;
bool operator <(const node&aa)const
{
return aa.x>x;
}
} a[maxn];
ll prevv[maxn];//prevv[i]表示从1~i有多少个货物,
ll prex[maxn];//prex[i]表示把1~i的货物全部搬到i的花费
ll sufv[maxn];//sufv[i]表示从i~n有多少个货物
ll sufx[maxn];//sufx[i]表示把i~n的货物全部搬到n的花费
ll culp(int l,int r)
{
return prex[r]-prex[l-1]-(prevv[l-1]*(a[r].x-a[l-1].x));
}
ll culs(int l,int r)
{
return sufx[l]-sufx[r+1]-(sufv[r+1]*(a[r+1].x-a[l].x));
}
int check(ll num)//枚举的答案
{
ll num1=num/2+1;
int l=1,r=2,mid=2;//这里的l和r是位置
while(1)
{
while(r<=n&&prevv[r]-prevv[l-1]<num)//还没有满足
r++;
while(mid<=n&&prevv[mid]-prevv[l-1]<num1)
mid++;
if(r>n||mid>n) break;
ll ans=culp(l,mid)+culs(mid,r-1)+(num-(prevv[r-1]-prevv[l-1]))*(a[r].x-a[mid].x);
if(ans<=t)
return 1;
l++;
}
l=n-1;
r=n;
mid=n;
while(1)
{
while(l>=1&&prevv[r]-prevv[l-1]<num)
l--;
while(mid>=2&&prevv[mid]-prevv[l-1]<num1)
mid--;
if(l<1||mid<2) break;
ll ans=culp(l+1,mid)+culs(mid,r)+(num-(prevv[r]-prevv[l]))*(a[mid].x-a[l].x);
if(ans<=t)
return 1;
r--;
}
return 0;
}
int main()
{
memset(prevv,0,sizeof(prevv));
memset(prex,0,sizeof(prex));
memset(sufv,0,sizeof(sufv));
memset(sufx,0,sizeof(sufx));
scanf("%d%lld",&n,&t);
ll l=1,r=0,mid;
t/=2;
for(int i=1; i<=n; i++)
{
scanf("%d",&a[i].x);//距离
}
for(int i=1; i<=n; i++)
scanf("%d",&a[i].v);//每个点的物品数量
sort(a+1,a+n+1);//按照位置前后来排序;
for(int i=1; i<=n; i++)
{
prevv[i]=prevv[i-1]+a[i].v;
prex[i]=prevv[i-1]*abs(a[i].x-a[i-1].x)+prex[i-1];
r+=a[i].v;
}
for(int i=n; i>=1; i--)
{
sufv[i]=sufv[i+1]+a[i].v;
sufx[i]=sufv[i+1]*abs(a[i].x-a[i+1].x)+sufx[i+1];
}
while(l<=r)//这里枚举的左右端点和中间点都是能搬运的货物的数量
{
mid=(l+r)/2;
if(check(mid)) l=mid+1;
else r=mid-1;
}
printf("%lld\n",r);
return 0;
}