1 条题解

  • 0
    @ 2025-8-24 23:06:57

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar unicorn1220
    这个家伙很菜,什么也没有留下

    搬运于2025-08-24 23:06:57,当前版本为作者最后更新于2025-08-07 11:37:33,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    [JOI Open 2019] Triple Jump

    题意

    给定一个长为 NN 的序列 AA。有 QQ 个查询,每次给定 L,RL,R,求出满足以下条件的三元组 (i,j,k)(i,j,k) 使得 Ai+Aj+AkA_i+A_j+A_k 最大。

    • Li<j<kRL \leq i<j<k \leq R
    • jikjj-i \leq k-j

    题解

    暴力枚举情况太多,所以考虑去掉某些无需枚举的情况。

    什么时候可以扔掉一个情况 p1(i1,j1,k1)p_1(i_1,j_1,k_1) 呢?当存在一个 p2(i2,j2,k2)p_2(i_2,j_2,k_2) 一定不劣于 p1p_1 时。

    这里的不劣于,需要考虑两个条件。第一,p2p_2 的贡献一定要比 p1p1 大。第二,如果 p1p1 对于一组 (L,R)(L,R) 合法,那么 p2p2 也合法,即区间 [i2,k2][i_2,k_2][i1,k1][i_1,k_1] 包含。

    也就是说,满足 $A_{i_1}+A_{j_1}+A_{k_1} \leq A_{i_2}+A_{j_2}+A_{k_2}$,且 i1i2<k2k1i_1 \leq i_2 < k_2 \leq k_1

    然而,这种约束情况一不太好找,二过于苛刻,可能会导致去掉的情况不够多。

    我们观察 jikjj-i \leq k-j 这个式子,显然当我们钦定 i,ji,j 时,kk 的取值范围是一个后缀,准确来说是 [2ji,n][2j-i,n]。这样的话,如果我们缩小区间 [i,j][i,j],那么 kk 的取值范围一定是只会变大的。

    我们惊奇的发现,这个缩小后的区间,正好对应了 p2p2。于是我们把约束条件中的 kk 去掉,变成 Ai1+Aj1Ai2+Aj2A_{i_1}+A_{j_1} \leq A_{i_2}+A_{j_2},且 i1i2<j2j1i_1 \leq i_2 < j_2 \leq j_1。这时,i,ji,j 的贡献保证了不劣,而因为 p2p2 的区间 [i2,j2][i_2,j_2] 更小,所以 k2k_2 的取值范围更大,也就保证了不劣。

    现在来看还剩下多少有用情况。上面的式子意味着对于 i,ji,j,如果他们之间存在 ii' 使得 Ai<AiA_i < A_{i'}Aj<AiA_j < A_{i'},那么 (i,j)(i,j) 就会被 (i,j)(i',j) 替代,它就没用了。即有用区间可以表示为 [i,j][i,j] 使得 min(a[i],a[j])>maxk=i+1j1(a[k])\min(a[i],a[j]) > \max^{j-1}_{k=i+1}(a[k])。我们令 LiL_iii 之前最后一个比 AiA_i 大的 ALiA_{L_i}RiR_iii 之后第一个比 AiA_i 大的 ARiA_{R_i}。此时,所有的 (Li,i](L_i,i][i,Ri)[i,R_i) 即可描述所有的有用情况,最多 2n2n 种。

    这么少的情况可以直接用线段树维护。对于所有有用情况 (li,ri)(l_i,r_i) ,区间 [2ji,n][2j-i,n] 的所有数都可以获得 Ali+AriA_{l_i}+A_{r_i} 的贡献,取 max\max 加上原本的数即为答案。而 Li,RiL_i,R_i 可以随便用单调栈维护。加上查询总复杂度为 O((n+q)logn)O((n+q)\log n)

    最后,在处理询问时,有用区间应该按左端点降序插入线段树,同时边插边把当前左端点的询问做了,因为我们要保证插入线段树的区间也在询问范围内。

    代码放个主函数吧。

    signed main()
    {
        scanf("%lld",&n);
        for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
        build(1,1,n);scanf("%lld",&Q);
        for(int i=1,ai,bi;i<=Q;i++) scanf("%lld%lld",&ai,&bi),ask[ai].push_back(make_pair(bi,i));
        for(int i=1;i<=n;i++)
        {
            while(!q.empty()&&a[i]>a[q.top()]) q.pop();
            if(!q.empty()) qj[q.top()].push_back(i);
            q.push(i);
        }//找Li
        while(!q.empty()) q.pop();
        for(int i=n;i>=1;i--)
        {
             while(!q.empty()&&a[i]>a[q.top()]) q.pop();
            if(!q.empty()) qj[i].push_back(q.top());
            q.push(i);
        }//找Ri
        for(int i=n;i>=1;i--)//倒着插入
        {
            for(int j:qj[i]) update(1,1,n,2*j-i,n,a[j]+a[i]);
            for(auto j:ask[i]) ans[j.second]=query(1,1,n,i,j.first);
        }
        for(int i=1;i<=Q;i++) printf("%lld\n",ans[i]);
        return 0;
    }
    
    • 1

    信息

    ID
    11113
    时间
    2000ms
    内存
    512MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者