时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
White Cloud placed n containers in sequence on a axes. The i-th container is located at x[i] and there are a[i] number of products in it.
White Rabbit wants to buy some products. The products which are required to be sold must be placed in the same container.
The cost of moving a product from container u to container v is 2*abs(x[u]-x[v]).
White Cloud wants to know the maximum number of products it can sell. The total cost can't exceed T.
输入描述:
The first line of input contains 2 integers n and T(n <= 500000,T <= 1000000000000000000) In the next line there are n increasing numbers in range [0,1000000000] denoting x[1..n] In the next line there are n numbers in range[0,10000] denoting a[1..n]
输出描述:
Print an integer denoting the answer.
示例1
输入
复制
2 3 1 2 2 3
输出
复制
4
题意:n个物品分布在X轴上,现在可以移动一些物品,移动物品的花费为2*abs(x[u]-x[v]).问在花费不超过T的情况下,最多有多少个物品在同一地方。
分析:二分最终在统一地方的物品的数目。范围是:[1,所有物品的数目之和]。
在二分之前,需要处理好几个数组:
prex数组:前i个物品的数目之和。
prew数组:前i个所有物品移动到第i个位置的总花费。
sufx数组:i-n的物品数目之和。
sufw:i-n的左右物品移动到n位置的总花费。
当确定好合并区间之后,找到二分过程中X在该区间中位数的位置mid(因为当你要合并区间内的物品时,当合并的位置为中位数的位置时花费最少)。然后分别计算出左端点到mid的花费,mid到右端点之间的花费。注意:当找到区间[l,r]时,不一定将该区间内的物品都移动到mid位置处,有可能该区间内的物品总和可能大于X。此时要判断左右端点。
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn=5e5+10;
typedef long long ll;
ll sufx[maxn],sufw[maxn],prex[maxn],prew[maxn];
int n;
ll t;
struct node
{
ll x;
ll num;
}edge[maxn];
bool cmp(node a,node b)
{
return a.x<b.x;
}
ll cul_pre(ll l,ll r)
{
return prex[r]-prex[l-1]-(edge[r].x-edge[l-1].x)*prew[l-1]; ///从左往右统计
}
ll cul_suf(ll l,ll r)
{
return sufx[l]-sufx[r+1]-(edge[r+1].x-edge[l].x)*sufw[r+1]; ///从右往左统计
}
bool check(ll x)
{
ll l=1,r=2,mid=2;
ll num=x/2+1;
while(1)
{
while(r<=n&&prew[r]-prew[l-1]<x) r++;
while(mid<=n&&prew[mid]-prew[l-1]<num) mid++; ///查找区间的中位数
if(r>n||mid>n) break;
ll dis=cul_pre(l,mid)+cul_suf(mid,r-1)+(x-(prew[r-1]-prew[l-1]))*(edge[r].x-edge[mid].x); ///右端点可能存在多余,应为是从右端点往左端点移动
if(dis<=t) return true;
l++;
}
l=n-1;
r=mid=n;
while(1)
{
while(l>=1&&prew[r]-prew[l-1]<x) l--;
while(mid>=2&&prew[mid]-prew[l-1]<num) mid--; ///查找区间的中位数
if(mid<2||l<1) break;
ll dis=cul_pre(l+1,mid)+cul_suf(mid,r)+(x-(prew[r]-prew[l]))*(edge[mid].x-edge[l].x); ///左端点可能存在多余,应为是从左端点往右端点移动
if(dis<=t)return true;
r--;
}
return false;
}
int main()
{
while(~scanf("%d%lld",&n,&t))
{
t/=2;
ll r=0;
for(int i=1;i<=n;i++)
scanf("%lld",&edge[i].x);
for(int i=1;i<=n;i++)
{
scanf("%lld",&edge[i].num);
r+=edge[i].num;
}
prex[0]=prew[0]=0;
sufw[n+1]=sufx[n+1]=0;
sort(edge+1,edge+1+n,cmp);
for(int i=1,j=n;i<=n;i++,j--)
{
prew[i]=prew[i-1]+edge[i].num;
prex[i]=prew[i-1]*(edge[i].x-edge[i-1].x)+prex[i-1];
sufw[j]=sufw[j+1]+edge[j].num;
sufx[j]=sufw[j+1]*(edge[j+1].x-edge[j].x)+sufx[j+1];
}
ll l=1;
while(l<=r)
{
ll mid=(l+r)>>1;
if(check(mid))
l=mid+1;
else
r=mid-1;
}
printf("%lld\n",r);
}
return 0;
}
/*
2 3
1 2
2 3
*/