牛客第二场-双二分+枚举左右端点+前缀和

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define ll long long
using namespace std;
const ll maxn = 500007;
ll n;
ll t;
ll x[maxn],a[maxn];
ll sum_num[maxn];
ll sum_t[maxn];
void init()
{
    sum_num[0]=0;
    sum_t[0]=0;
    for (int i=1; i<=n; i++)
    {
        scanf("%lld",&x[i]);
    }
    for (int i=1; i<=n; i++)
    {
        scanf("%lld",&a[i]);
        sum_num[i] = sum_num[i-1]+a[i];//前i项物品总和
        sum_t[i] = sum_t[i-1]+2*a[i]*x[i];//物品运送到0需要花费的体力
    }
}
ll le(ll l,ll r)
{
    return (sum_t[r]-sum_t[l-1])-(sum_num[r]-sum_num[l-1])*x[l]*2;//看成先移到0,再移到l
}
ll rr(ll l,ll r)
{
    return (sum_num[r]-sum_num[l-1])*x[r]*2-(sum_t[r]-sum_t[l-1]);//看成先移到0,再移到r,由于必为正值所以取相反数
}
bool jug(ll ans){
   ll l,r,mid,mid_num;
   mid_num=ans/2+1;
   l=r=mid=1;
   while(1){//枚举右端点
    while(r<=n && (sum_num[r]-sum_num[l-1]<ans))r++;//枚举一个合理的r
    while(mid<r && (sum_num[mid]-sum_num[l-1])<mid_num)mid++;//再枚举这个合理mid
    if(r>n || mid>n)break;
    ll tmp = (sum_num[r]-sum_num[l-1])-ans;
    if (rr(l,mid)+le(mid,r)-2*tmp*(x[r]-x[mid])<=t)//满足情况
        return 1;
    l++;//后左边更新
   }
   l=r=mid=n;
   while(1)//先枚举右端点
   {
       while(l>=1 && (sum_num[r]-sum_num[l-1])<ans)l--;//枚举合理的l
       while(mid>l && (sum_num[r]-sum_num[mid-1]<mid_num))mid--;
       if (l<1 || mid<l)break;
       ll tmp =(sum_num[r]-sum_num[l-1])-ans;
       if (rr(l,mid)+le(mid,r)-2*tmp*(x[mid]-x[l])<t)
        return 1;
       r--;
   }
   return 0;
}
void solve()
{
    ll l,r,mid,res;
    l=1;
    r=sum_num[n];
    while(l<=r)//二分答案
    {
        mid=(l+r)/2;
        if (jug(mid))//验证是否是更优秀的
        {
            res=mid;
            l=mid+1;
        }
        else
        {
            r=mid-1;
        }
    }
    printf("%lld\n",res);
}
int main()
{
    while(~scanf("%lld%lld",&n,&t))
    {
        init();
       solve();
    }
    return 0;
}

 

posted @ 2018-09-05 19:44  bluefly-hrbust  阅读(242)  评论(0编辑  收藏  举报