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

时间限制: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
*/

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值