牛客网多校2 transform(二分+尺取)

题目:在一个数轴上有n个集装箱,第 i 个集装箱的位置为x[i],且在集装箱内装有w[i]件货物,现在将这些集装箱内的货物进行移动(将一件货物从第 i 个集装箱移动到第 j 个集装箱的花费就为2*abs(x[i]-x[j]) ),求在总花费不超过T的情况下,最多能将多少货物移动到同一个集装箱内。

思路:二分一下num个集装箱被移到一起,用尺取判断是否存在可行方案。判断时由num可以确定区间的长度,先按左端点这别的货物都移过去最优时判断,再按右端点优先。因为符合贪心性质,所以肯定会有一个最优的。当选定一个区间时,最终货物集合的地点肯定是中位数(num/2+1)这个位置,注意不是中间位置。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=5e5+10;
struct node{
    ll x,w;
}a[maxn];
bool cmp(const node &a,const node &b)
{
    return a.x<b.x;
}
ll prew[maxn],pres[maxn],sufw[maxn],sufs[maxn];
int n;
ll m;
ll getsum1(int l,int r)
{
    ll ans=pres[r]-pres[l];
    ans-=2*prew[l-1]*(a[r].x-a[l].x);
    return ans;
}
ll getsum2(int l,int r)
{
    ll ans=sufs[l]-sufs[r];
    ans-=2*sufw[r+1]*(a[r].x-a[l].x);
    return ans;
}
int check(int num)
{
    int l=1,r=1,mid=1;
    while(r<=n)
    {
        while(r<=n&&prew[r]-prew[l-1]<num) r++;
        while(mid<=n&&prew[mid]-prew[l-1]<num/2+1) mid++;
        if(r>n||mid>n) break;
        ll sum=getsum1(l,mid);
        sum+=getsum2(mid,r-1);
        sum+=2*(num-(prew[r-1]-prew[l-1]))*(a[r].x-a[mid].x);
        if(sum<=m) return true;
        l++;
    }
    l=n,r=n,mid=n;
    while(l>0)
    {
        while(l>0&&sufw[l]-sufw[r+1]<num) l--;
        while(mid>0&&sufw[mid]-sufw[r+1]<num/2+1) mid--;
        if(l<1||mid<1) break;
        ll sum=getsum1(l+1,mid);
        sum+=getsum2(mid,r);
        sum+=2*(num-(sufw[l+1]-sufw[r+1]))*(a[mid].x-a[l].x);
        if(sum<=m) return true;
        r--;
    }
    return false;
}
int main()
{
    scanf("%d%lld",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i].x);
    ll S=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i].w);
        S+=a[i].w;
    }
    sort(a+1,a+n+1,cmp);
    memset(sufw,0,sizeof sufw);
    memset(sufs,0,sizeof sufs);
    memset(prew,0,sizeof prew);
    memset(pres,0,sizeof pres);
    a[0]=a[1];a[n+1]=a[n];
    for(int i=1;i<=n;i++)
    {
        prew[i]=prew[i-1]+a[i].w;
        pres[i]=pres[i-1]+2ll*prew[i-1]*(a[i].x-a[i-1].x);
    }
    for(int i=n;i>=1;i--)
    {
        sufw[i]=sufw[i+1]+a[i].w;
        sufs[i]=sufs[i+1]+2ll*sufw[i+1]*(a[i+1].x-a[i].x);
    }
    ll l=1,r=S,ans=0;
    while(l<=r)
    {
        ll mid=(l+r)>>1;
        if(check(mid))
        {
            ans=mid;
            l=mid+1;
        }
        else
            r=mid-1;
    }
    printf("%lld\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/dllpxfire/article/details/81280485