题目链接:点击这里
解题思路:
第一种:如果没有花费限制的话,那么都集中在中位数那里一定是最优的。所以我们可以二分答案然后去找可行区间在那里面就可以直接集中到中位数一定是最优的。
第二种:我们主要说第二种骚操作,从左到右枚举集中点,当前最优状态可以大部分继承上一个状态的最优状态。用两个指针维护最优区间,也就是集中点左边最远的距离和右边最远的距离。
这样维护居然跑了100多MS,比二分快多了= =!
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<set>
#define inf 0x3f3f3f3f
using namespace std;
const int mx = 5e5 + 10;
typedef long long ll;
ll n,T,x[mx],a[mx],sum[mx],m,cnt[mx];
int main()
{
while(~scanf("%lld%lld",&n,&m))
{
for(int i=1;i<=n;i++) scanf("%lld",x+i);
for(int i=1;i<=n;i++) scanf("%lld",a+i),cnt[i] = cnt[i-1] + a[i];
int L = 1,R = 1;
x[n+1] = 1e18;
ll c = a[1],ans = a[1],keep = m,lv,rv;
for(int i=2;i<=n;i++){
if(keep<=2*a[i]*(x[i]-x[1])){
ans = c + keep/(2*(x[i]-x[1]));
break;
}else keep -= 2*a[i]*(x[i]-x[1]),c += a[i],R++;
}
ans = max(ans,c);
for(int i=2;i<=n;i++){
if(L!=R) keep += (cnt[R]-2*cnt[i-1]+cnt[L-1])*2*(x[i]-x[i-1]);
else L = R = i,c = a[i];
while(R!=n&&(x[i]-x[L])>(x[R+1]-x[i])) keep += (x[i]-x[L])*2*a[L],c -= a[L],L++;
while(L!=1&&(x[i]-x[L-1])<(x[R]-x[i])) keep += (x[R]-x[i])*2*a[R],c -= a[R],R--;
while(keep<0){
lv = x[i]-x[L],rv = (x[R]-x[i]);
if(lv>=rv){
keep += lv*2*a[L];
c -= a[L++];
}else keep += rv*2*a[R],c -= a[R--];
}
while(L!=1||R!=n){
lv = (x[i]-x[L-1]),rv = (x[R+1]-x[i]);
if(L==1||rv<=lv)
{
if(keep<=2*rv*a[R+1]){
ans = max(ans,c+keep/(2*rv));
break;
}else R++,keep -= 2*rv*a[R],c += a[R];
}else{
if(keep<=2*lv*a[L-1]){
ans = max(ans,c+keep/(2*lv));
break;
}else L--,keep -= 2*lv*a[L],c += a[L];
}
}
ans = max(ans,c);
}
printf("%lld\n",ans);
}
return 0;
}