這道題的思路:
預處理出物品個數的前綴和、後綴和,以及 將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;
}