牛客多校赛 G transform(二分)
https://blog.csdn.net/Lee_w_j__/article/details/81157488
题目描述
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个集装箱,第 i 个集装箱的位置为x[i],且在集装箱内装有a[i]件货物,现在将这些集装箱内的货物进行移动(将一件货物从第 i 个集装箱移动到第 j 个集装箱的花费就为2*abs(x[i]-x[j]) ),求在总花费不超过T的情况下,最多能将多少货物移动到同一个集装箱内。
题目思路:既然要使得花费在不超过T的情况尽可能多的移动货物,那么我们肯定是将一个区间内的所有货物移到坐标中位的集装箱上。那么我们就可以对答案进行二分,然后枚举所要移动的区间的左端点,再找到中位点和右端点,然后判断这个区间移动的花费是否小于T。
#include<iostream>
#include<bits/stdc++.h>
#include<stdio.h>
#include<cmath>
#include<string.h>
#define mod 1000000000
#define ll long long
using namespace std;
ll n,k;
struct code
{
ll x,v;
} point[500005];
bool cmp(code a,code b)
{
return a.x<b.x;
}
ll prenum[500005],prevv[500005],sufnum[500005],sufv[500005];
ll cal_pre(ll l,ll r)//l-r区间移动r位置的费用
{
return prevv[r]-prevv[l-1]-prenum[l-1]*(point[r].x-point[l-1].x);
}
ll cal_suf(ll l,ll r)//l-r区间移动到l位置的费用
{
return sufv[l]-sufv[r+1]-sufnum[r+1]*(point[r+1].x-point[l].x);
}
bool check(ll sum)
{
ll sum2=(sum+1)/2;
ll l=1,r=1,mid=1;
while(1)//从前向后枚举区间
{
while(r<=n&&prenum[r]-prenum[l-1]<sum)//凑够数量
r++;
while(mid<=n&&prenum[mid]-prenum[l-1]<sum2)//找中间位置
mid++;
if(r>n||mid>n)
break;
ll tx=prenum[r]-prenum[l-1]-sum;//多余枚举答案的数量
ll cost=cal_pre(l,mid)+cal_suf(mid,r)-tx*(point[r].x-point[mid].x);
if(cost<=k)
return true;
l++;
}
l=n,r=n,mid=n;
while(1)//从后向前枚举区间
{
while(l>=1&&sufnum[l]-sufnum[r+1]<sum)
l--;
while(mid>=1&&sufnum[mid]-sufnum[r+1]<sum2)
mid--;
if(l<1||mid<1)
break;
ll tx=sufnum[l]-sufnum[r+1]-sum;
ll cost=cal_pre(l,mid)+cal_suf(mid,r)-tx*(point[mid].x-point[l].x);
if(cost<=k)
return true;
r--;
}
//两个方向枚举是因为中间点的不同
return false;
}
int main()
{
scanf("%lld%lld",&n,&k);
k/=2;
for(int i=1; i<=n; i++)
scanf("%lld",&point[i].x);
for(int i=1; i<=n; i++)
scanf("%lld",&point[i].v);
sort(point+1,point+n+1,cmp);
prenum[0]=point[0].v;
prevv[0]=0;
for(int i=1; i<=n; i++)
{
prenum[i]=prenum[i-1]+point[i].v;
prevv[i]=prenum[i-1]*(point[i].x-point[i-1].x)+prevv[i-1];
}//前缀,i前面所有点移动到i的数量和花费
sufnum[n+1]=0;
sufv[n+1]=0;
for(int i=n; i>0; i--)
{
sufnum[i]=sufnum[i+1]+point[i].v;
sufv[i]=sufnum[i+1]*(point[i+1].x-point[i].x)+sufv[i+1];
}//后缀,i后面所有点移动到i的数量和花费
ll l=0,r=prenum[n];
while(l<=r)
{
ll mid=(l+r)/2;
if(check(mid))
l=mid+1;
else
r=mid-1;
}
printf("%lld\n",r);
return 0;
}
上一篇: 牛客竞赛——借教室(二分+差分)
下一篇: java通过链表实现队列,先进先出
推荐阅读
-
牛客多校赛 G transform(二分)
-
牛客多校第三场 G. Operating on a Graph(并查集,链表)
-
牛客小白月赛25 (C 换根 D 概率 G 二分 J 异或操作)
-
牛客多校第二场 G transform
-
牛客多校第四场 G Maximum Mode
-
2020暑期牛客多校第二场G.Greater and Greater(bitset+思维构造)
-
2020牛客多校第二场 G - Greater and Greater(思维+bitset)
-
2020牛客多校 2G.Greater and Greater(双指针+bitset优化)
-
2020牛客多校二 G. Greater and Greater (双指针+bitset优化)
-
2020牛客多校 G - Greater and Greater 动态规划+bitset优化