牛客网暑期ACM多校训练营(第二场)G-transform

链接:https://www.nowcoder.com/acm/contest/140/G
来源:牛客网
 

题目描述

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.

输入

2 3
1 2
2 3

输出

4

题意:

  在数轴上有N个箱子,每个箱子在坐标dis[i],有num[i]件物品,

  现在我们对//物品//,进行搬运,问在总搬运距离不超过T的情况下,最多能把多少个//物品//移动到同一个位置。

思路:

假如选定了一个位置,最后搬运的箱子一定是连续的区间,最优点flag的位置是区间的中点,毕竟不可能越过近的去拿相对远的那个,而且本题搬运的是//物品//,并不是集装箱所以我们要判断比需求量多的那部分是从左端点移过来的,还是从右端点移过来的,本题我们可以先二分答案,找到可能的位置再根据花费的大小情况继续进行二分,即可求解

在具体处理的时候,处理下前缀和,类似于现将所有物品虚拟扔在原点计算,

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>

#define inf 0x3f3f3f3f

const int maxn=5e5+86;

long long dis[maxn];  //第i个集装箱到0的距离
long long num[maxn];  //第i个集装箱装有产品的个数
long long sum[maxn];  //前i个集装箱所有物品的总和
long long pay[maxn];  //前i个集装箱所有物品移动到X处消耗的费用

long long n,t;
long long q;
long long l,r,flag;

long long cost_l(int i)//优先取左
{
    return   ((sum[i]-sum[l])*dis[i]-(pay[i]-pay[l]))+((pay[r]-pay[i])-(sum[r]-sum[i])*dis[i])-(sum[r]-sum[l]-q)*(dis[r]-dis[i]);
    //       将i点到区间左的所有物品移动到i点的花费   //+将i点到区间右的所有物品移动到i点的花费     //-多于二分答案mid的物品移动到i点的花费
}

long long cost_r(int i)//优先取右
{
    return   ((pay[r]-pay[i])-(sum[r]-sum[i])*dis[i])+((sum[i-1]-sum[l])*dis[i]-(pay[i-1]-pay[l]))-(sum[r]-sum[l]-q)*(dis[i]-dis[l+1]);
    //       将i点到区间右的所有物品移动到i点的花费   //+将i点到区间左的所有物品移动到i点的花费         //-多于二分答案mid的物品移动到i点的花费
}

bool check(long long &x)
{
    q=x;

    //从左边界开始向右移动区间,优先取左侧物品
    l=0,r=1,flag=1;
    while(true)
    {
        while (r<n&&sum[r]-sum[l]<x) r++;
        if (sum[r]-sum[l]<x) break; //如果l到r不能满足条件,那么l++到n也不能满足
        if (flag<l) flag=l;
        while (flag<r&&cost_l(flag)>cost_l(flag+1)) flag++;
        if(cost_l(flag)<=t) return true;
        l++;
    }

    //从右边界开始向左移动区间,优先取右侧物品
    l=n-1,r=n,flag=n;
    while (true)
    {
        while (l>0&&sum[r]-sum[l]<x) l--;
        if (sum[r]-sum[l]<x) break; //如果l到r不能满足条件,那么r--到n也不能满足
        if(flag >r) flag=r;
        while (flag>l&&cost_r(flag)>cost_r(flag-1)) flag--;
        if(cost_r(flag)<=t) return true;
        r--;
    }

    return false;
}

int main()
{
    long long L,R,mid;
    scanf("%lld %lld",&n,&t);
    t/=2;
    for(int i = 1; i <= n ; ++i) scanf("%lld",&dis[i]);
    for(int i = 1; i <= n ; ++i)
    {
        scanf("%lld",&num[i]);
        sum[i]=sum[i-1]+num[i];
        pay[i]=pay[i-1]+num[i]*dis[i];
    }
    L=0;
    R=sum[n]+1;
    while (R-L>1)
    {
        mid=L-(L-R)/2;//二分一个答案,不断地判断答案是否满足
        if(check(mid)) L=mid;
        else R=mid;
    }
    printf("%lld",L);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/leper_gnome/article/details/81191243