13946 问题 E: 积木游戏

本文介绍了一种积木游戏的解决方案,通过贪心策略选择最近的积木,并采用二分查找优化算法,减少时间复杂度。文章讨论了如何维护区间积木的前缀和来高效计算时间花费,并解释了如何通过调整二分查找的过程来进一步降低时间复杂度,最终实现算法在1秒内运行完成。
摘要由CSDN通过智能技术生成
思路

首先按照坐标x排序,从左到右。
时间可以直接除以二,因为拿回来摆放积木需要来回移动,就需要距离的两倍作为移动时间,因此可以将总时间和移动的时间同时除以二,即,移动时间为距离,总时间除以二。
贪心选取最近的积木:由于所有积木得分都是一样的(权值相同),而时间则是近的比远的少,所以在可以选择两种不同的积木的时候,优先选择距离当前点近的,不会得到更差的结果。
在每一个节点,对于固定的时间,可以找到最远的距离,使得在这个距离内的所有积木都拿完,然后使用剩下的时间拿(当前距离+1)上的积木,拿到的积木个数是 剩 余 时 间 当 前 距 离 + 1 \frac{剩余时间}{当前距离+1} +1。显然这部分积木无法拿光,因为如果拿光这个距离就不再是最远的。特别地,如果这个距离内已经拿走了所有积木,也不可以继续拿积木了,可以对答案做一个与积木总数取最小值处理。
那么我们需要维护一下拿完一段区间所有积木所需要的时间花费。这个花费就是这个位置的积木个数乘距离差。我将左右两侧分开维护,以右侧为例,一个数组ry维护积木个数的前缀和,一个数组rs维护积木个数与第一个点距离差的前缀和。查询时只要用rs的区间和减去ry的区间和与当前点和第一个点距离差的乘积就可以得到这部分时间花费。左侧与右侧对称即可,即前缀和变后缀和。
有了这样的预处理,就可以先枚举每一个位置作为起点,再二分距离,找上述最远距离。二分距离的过程中,再分别二分获取左右两侧到达的最远的下标(有距离但是要知道不超过距离的最大下标是多少),然后用这个区间更新答案。
目前时间复杂度为 O ( n l o g m l o g n ) O(nlogmlogn) O(nlogmlogn),其中m为x的最大值,但是这个时间还是过不去
考虑两次二分的关系,第一次二分使得答案范围缩小一半的同时,也缩小了第二次二分的答案范围,采取nl1,nr1等存储当前的二分答案范围,如果该侧范围缩小,则使其等于l1或r1,否则,使l1或r1等于当前的范围,这样复杂度可以进一步降低,能在1s内运行结束。

代码

由于时间比较极限,我开了所有能想到的优化常数的方法,因此可读性会有所降低,这些方法包括O3,register,定义放在循环外节省开空间的时间,fread,define替代变量赋值;而int128主要为了防止出现炸long long的情况。

#pragma GCC optimize(3)
#include  <bits/stdc++.h>
#define rint register int
using namespace std;
typedef long long ll;
const int N=5e5+5;
const int inf=0x3f3f3f3f;
struct ios {
    inline char gc(){
        static const int IN_LEN=1<<18|1;
        static char buf[IN_LEN],*s,*t;
        return (s==t)&&(t=(s=buf)+fread(buf,1,IN_LEN,stdin)),s==t?-1:*s++;
    }
 
    template <typename _Tp> inline ios & operator >> (_Tp&x){
        static char ch,sgn; ch = gc(), sgn = 0;
        for(;!isdigit(ch);ch=gc()){if(ch==-1)return *this;sgn|=ch=='-';}
        for(x=0;isdigit(ch);ch=gc())x=x*10+(ch^'0');
        sgn&&(x=-x); return *this;
    }
} io;
int n;
ll t;
struct data
{
    int a,x;
}a[N];
bool cmp(data a,data b)
{
    return a.x<b.x;
}
__int128 rs[N],ls[N],ry[N],ly[N];
int l,r,l1,r1,l2,r2,mid,m1,m2,lf,rt,nl1,nl2,nr1,nr2;
int main()
{
#ifndef ONLINE_JUDGE
    freopen("in","r",stdin);
#endif
 
    __int128 ans=0;
    io>>n>>t;
    t/=2;
    for(rint i=1;i<=n;i++)
        io>>a[i].x;
    for(rint i=1;i<=n;i++)
        io>>a[i].a;
    sort(a+1,a+1+n,cmp);
    lf=a[1].x,rt=a[n].x;
    for(rint i=1;i<=n;i++)
    {
        rs[i]=__int128(a[i].x-lf)*a[i].a+rs[i-1],ry[i]=a[i].a+ry[i-1];
    }
    for(rint i=n;i>=1;i--)
    {
        ls[i]=__int128(rt-a[i].x)*a[i].a+ls[i+1],ly[i]=a[i].a+ly[i+1];
    }
    for(rint i=1;i<=n;i++)
    {
        l=0,r=max(a[i].x-lf,rt-a[i].x),l1=i,r1=1,l2=i,r2=n,nl1=i,nr1=1,nl2=i,nr2=n;
        while(l<r)
        {
            mid=(l+r+1)>>1;
            while(l1>r1)
            {
                m1=(l1+r1)>>1;
                if(a[i].x-a[m1].x>mid) r1=m1+1;
                else l1=m1;
            }
            while(l2<r2)
            {
                m2=(l2+r2+1)>>1;
                if(a[m2].x-a[i].x>mid) r2=m2-1;
                else l2=m2;
            }
#define sumr ((rs[l2]-rs[i])-(ry[l2]-ry[i])*(a[i].x-lf))
#define suml ((ls[l1]-ls[i])-(ly[l1]-ly[i])*(rt-a[i].x))
            if(suml+sumr>t)
            {
                r=mid-1,l1=nl1,l2=nl2,nr1=r1,nr2=r2;
            }
            else
            {
                l=mid,r1=nr1,r2=nr2,nl1=l1,nl2=l2;
            }
        }
#define mid l
        while(l1<r1)
        {
            m1=(l1+r1)>>1;
            if(a[i].x-a[m1].x>mid) r1=m1+1;
            else l1=m1;
        }
        while(l2<r2)
        {
            m2=(l2+r2+1)>>1;
            if(a[m2].x-a[i].x>mid) r2=m2-1;
            else l2=m2;
        }
        //cout<<i<<' '<<(ll)((t-suml-sumr)/(l+1))<<' '<<(ll)(ry[r2]-ry[l1-1])<<endl;
        ans=max(ans,(t-suml-sumr)/(l+1)+ry[r2]-ry[l1-1]);
        ans=min(ans,ry[n]);
    }
    printf("%lld",(ll)ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值