重新填坑——牛客暑期多校第二場 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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章