欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

牛客多校赛 G transform(二分)

程序员文章站 2024-03-17 15:04:52
...

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;
}