牛客第二场-双二分+枚举左右端点+前缀和
#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; }