重新填坑——牛客暑期多校第二场 G Transform(二分+预处理)

这道题的思路:

预处理出物品个数的前缀和、后缀和,以及 将1到i-1位置的物品全部搬到i位置的花费和 与 将n到n-i+1的物品全部搬到i位置的花费和,然后对要求的物品个数二分答案。

check的时候,因为物品个数确定,枚举左端点,左端点位置确定的时候右端点位置和中间值位置也确定,因此可以通过预处理的东西直接计算出全部搬到中间值位置的花费。

但是对于为什么只需要求左端点的箱子都取、右端点在的箱子取部分 和 右端点在的箱子都取、左端点在的箱子取部分就能得到最小花费我还无法理解。。。(纠结了很久了,就是想不清楚= =)

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define db double
#define m_p make_pair
#define p_b push_back
#define For(i,a,b) for(int i=a;i<=b;i++)
#define ls (root<<1)
#define rs ((root<<1)|1)
const int N=5e5;
const int M=1e4+5;
const db eps=1e-8;
const int INF=0x3f3f3f3f;
const int mod=1e7;
int n;
struct node{
    ll x,a;
    bool operator<(const node &p)const{
        return x<p.x;
    }
}a[N+5];
ll T,s[N+5],s1[N+5],pre[N+5],nex[N+5];
bool check(ll num){
    ll lpos=1,rpos=1,mid=1;
    ll num1=(num+1)/2;
    while(rpos<=n&&mid<=n){
        while(rpos<=n&&s[rpos]-s[lpos-1]<num){
            rpos++;
        }
        if(rpos>n) break;
        while(mid<=n&&s[mid]-s[lpos-1]<num1) mid++;
        if(mid>n) break;
        ll res=pre[mid]-pre[lpos]-2*s[lpos-1]*(a[mid].x-a[lpos].x);
        res+=nex[mid]-nex[rpos-1]-2*s1[rpos]*(a[rpos-1].x-a[mid].x);
        res+=2*(num-(s[rpos-1]-s[lpos-1]))*(a[rpos].x-a[mid].x);
        if(res<=T) return 1;
        lpos++;
    }
    rpos=n,mid=n,lpos=n;
    while(lpos>=1&&mid>=1){
        while(lpos>=1&&s1[lpos]-s1[rpos+1]<num) lpos--;
        if(lpos<1) break;
        while(mid>=1&&s1[mid]-s1[rpos+1]<num1) mid--;
        if(mid<1) break;
        ll res=pre[mid]-pre[lpos+1]-2*s[lpos]*(a[mid].x-a[lpos+1].x);
        res+=nex[mid]-nex[rpos]-2*s1[rpos+1]*(a[rpos].x-a[mid].x);
        res+=2*(num-(s[rpos]-s[lpos]))*(a[mid].x-a[lpos].x);
        if(res<=T) return 1;
        rpos--;
    }
    return 0;
}
int main(){
//  freopen("in.txt","r",stdin);
    scanf("%d %lld",&n,&T);
    For(i,1,n) scanf("%lld",&a[i].x);
    For(i,1,n) scanf("%lld",&a[i].a);
    sort(a+1,a+1+n);
    ll l=1,r=0,mid,ans=0;
    For(i,1,n) r+=a[i].a,s[i]=r,pre[i]=2*s[i-1]*(a[i].x-a[i-1].x)+pre[i-1];
    for(int i=n;i>=1;i--) s1[i]=s1[i+1]+a[i].a,nex[i]=nex[i+1]+2*s1[i+1]*(a[i+1].x-a[i].x);
    while(l<=r){
        mid=(l+r)>>1;
        if(check(mid)) l=mid+1,ans=mid;
        else r=mid-1;
    }
    cout<<ans<<"\n";
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_38515845/article/details/89331774
今日推荐