牛客多校第二场 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.

示例1

输入

2 3
1 2
2 3

输出

4


https://www.nowcoder.com/discuss/88268?type=101&order=0&pos=2&page=0
https://blog.csdn.net/wookaikaiko/article/details/81177870
输入:n T
   x[0].x[1],x[2]...
   a[0].a[1].a[2]...
题意:给出n个箱子,每个箱子里的产品个数a[i]不同, 我们把一个箱子里的物品移到另一个箱子里去需要2*abs(x[i]-x[j])的花费,问在花费不超过T的前提下,可以移动到同一个位置的物品最多多少

思路:我们想到我们要尽量把其他的物品移到一个位置,我们先按数轴位置排一个序,然后我们有两个问题
1.移到哪 :我们肯定选取的是物品数中位数所在得箱子里,这个是显而易见得
2.哪些移到那里   :我们肯定是把与中位数相邻的一些数移过来,因为其他位置更远,显然花费更大,划不来,所以肯定是一个区间,我们找出那个区间的中位数

方法:我们实现的主要方法呢就是我们二分那个最多物品数,然后我们判断用这个物品数是否能找到那个把其他数移到中位数的区间,如果找到,我们再尝试更大的物品数,否则放小
找寻那个区间的方法:我们枚举那个左端点,再枚举长度,直到找到那个移到中位数花费不超过T的物品数
我们定义四个数组 prec prew sufc sufw
prew存的是前i个物品数 所以递推式是 prew[i]=prew[i-1]+a[i];
prec存的是前i个移到当前位置的花费 所以递推式是 prec[i]=prec[i-1]+prew[i-1]*(x[i]-x[i-1])
(因为prec[i]是把前i个物品移到当前的花费是多少,说明我们之前已经把所有的物品移到了i-1位置,所以我们只要把所有物品数prew[i-1]*(i-1和i之间的相隔位置))

sufw存的是i到n的物品数,两个数组和上面的方法一样,只是方向变了,所以不再做赘述

我们如何求【l,r】的花费呢
prec[r]-prec[l-1]-prew[l-1]*(x[i]-x[l-1])
(因为prec[r]计算了1-l-1这一部分的物品从1移到r的花费,而prec[l-1]只有1-l-1这一段距离的花费,所以我们要格外的减去)

下面看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e6+10;
struct node
{
    ll x,w;
}a[N];
using namespace std;
ll n,T;
ll prew[N],prec[N],sufw[N],sufc[N];
ll cal_pre(ll l,ll r)
{
    return prec[r]-prec[l-1]-prew[l-1]*(a[r].x-a[l-1].x);
}
ll cal_suf(ll l,ll r)
{
    return sufc[l]-sufc[r+1]-sufw[r+1]*(a[r+1].x-a[l].x);
}
bool check(ll num)//因为我一个区间里的数不一定都要移到中位数来,有可能是左端点剩下了,有可能是右端点剩下了,所以我们分两种情况,
{
    ll num2=num/2+1;
    ll l=1,r=1,mid=1;
    while(1)
    {
        while(r<=n&&prew[r]-prew[l-1]<num)r++;//计算直到物品数大于我们假想的结果那一段区间
        while(mid<=n&&prew[mid]-prew[l-1]<num2)mid++;//求出中位数的位置
        if(r>n||mid>n)break;
        ll s=cal_pre(l,mid)+cal_suf(mid,r-1)+(num-(prew[r-1]-prew[l-1]))*(a[r].x-a[mid].x);// 右端点可能会多plus个product,所以我们要减去没用到的的一部分的花费
        if(s<=T)return true;
        l++;
    }
    l=r=mid=n;
    while(1)
    {
        while(l>=1&&prew[r]-prew[l-1]<num)l--;//下面的计算方法同上,只是剩下的是左端点
        while(mid>=2&&prew[mid]-prew[l-1]<num2)mid--;
        if(l<1||mid<2)break;
        ll s=cal_pre(l+1,mid)+cal_suf(mid,r)+(num-(prew[r]-prew[l]))*(a[mid].x-a[l].x);
        if(s<=T)return true;
        r--;
    }
    return false;
}
int main()
{
    scanf("%lld%lld",&n,&T);
    T/=2;
    ll l=0,r=0;
    for(ll i=1;i<=n;i++)
    {
        scanf("%lld",&a[i].x);
    }
    for(ll i=1;i<=n;i++)
    {
        scanf("%lld",&a[i].w);
    }
    for(ll i=1;i<=n;i++)
    {
        prew[i]=prew[i-1]+a[i].w;//计算左边的物品到当前位置的相关操作
        prec[i]=prec[i-1]+prew[i-1]*(a[i].x-a[i-1].x);
    }
    for(ll i=n;i>=1;i--)
    {
        sufw[i]=sufw[i+1]+a[i].w;//计算右边的物品到当前位置的相关操作
        sufc[i]=sufc[i+1]+sufw[i+1]*(a[i+1].x-a[i].x);
        r+=a[i].w;
    }
    while(l<r)
    {
        ll mid=(l+r+1)>>1;
        if(check(mid))
        {
            l=mid;
        }
        else r=mid-1;
    }
    printf("%lld\n",l);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Lis-/p/9377364.html