CF1523H Hopping Around the Array 题解

前言

前置知识:ST 表,倍增,背包 DP,贪心。

题目传送门

解析

考虑 \(k=0\)

\(k=0\) 时显然是可以用倍增解法的,基于贪心的想法发现每一步的最优走法唯一,所以可以直接记录下来做倍增。

贪心解释

最优走法就是走 \(p+a_p=\max\limits_{j\in [i,i+a_i]} j+a_j\) 中的 \(p\) 位置。

  1. 对于 \(i\le q<p\) 的位置 \(q\)。显然,除开 \(p,q\) 的可及范围的重叠部分,剩下的只在 \(q\) 中的部分在 \(p\) 之前,可及范围仍然没有 \(p\) 远。

  2. 对于 \(p< q\le i+a_i\) 的位置 \(q\)。其可及范围被 \(p\) 包含了,也不优。

同时,贪心策略的问题是,如果终点就在眼前,就不用再去踩可及范围最远的位置,而是直接去终点。所以倍增的时候,保持可及范围不覆盖终点,最后两步再过去。

所以倍增数组记为 \(f_{i,j}\) 表示在 \(j\) 位置连续跳 \(2^i\) 次,记录两个信息 \(x\), \(y\),表示可及最远和所在位置,以 \(x\) 为第一关键字。

struct Node {
    int x,y;
    Node operator+ (const Node &h)const {return x<h.x?h:*this;}
}f[LG][N];

似乎只会用 ST 表预处理了。


考虑 \(k>0\)

对于此情况,如果在 \(i\) 的可及范围内删一个位置,等同于把 \(a_i\) 加 1。

那么,现在就要考虑对于每个位置其可及范围内删了几个位置。由于 \(k\) 很小,考虑把这个信息记录进倍增数组。

先改为 \(f_{i,j,k}\) 表示 \(j\) 位置连续跳 \(2^i\) 次,中间删了 \(k\) 个点。

由于每个被删位置给只会给某些位置增加可及范围,所以在倍增时还要枚举前 \(2^{i-1}\) 次和后 \(2^{i-1}\) 次的删除个数,加起来为 \(k\),类似背包。

转移方程(注意重载了运算符):

\[f_{i,j,k}=f_{i,j,k}+f_{i-1,f_{i-1,j,x},k-x} \]

即:

for (int i=1;i<=lg[n];i++)
    for (int j=1;j<=n;j++)
        for (int k=0;k<=30;k++)
            for (int x=0;x<=k;x++)
                f[i][j][k]=f[i][j][k]+f[i-1][f[i-1][j][x].y][k-x];

对于询问,则仿照以上方法,维护出删除 \(0\)\(k\) 个的位置时,分别走到的位置,同时不能使某种情况的可及范围超过终点,然后考虑最优转移。

while (q--){
    int l=read(),r=read(),up=read(),ans=2;
    if (l==r) {puts("0");continue;}
    if (l+a[l]+up>=r) {puts("1");continue;}//特判
    for (int i=0;i<=up;i++) pos[i]=l;
    for (int i=lg[n];i>=0;i--){
        bool flag=1;
        for (int j=0;j<=up;j++) 
            if (f[i][pos[j]][up-j].x>=r) {flag=0;break;}//限制可及范围
        if (!flag) continue;
        ans+=(1<<i);
        for (int k=up;k>=0;k--){
            Node res={0,0};
            for (int x=0;x<=k;x++) res=res+f[i][pos[x]][k-x];//得到最优转移
            pos[k]=res.y;
        }
    }
    write(ans);putchar('\n');
}

时间复杂度 \(O(nk^2\log n+qk^2\log n)\),空间复杂度 \(O(nk\log n)\)

#include <bits/stdc++.h>
using namespace std;
 
#define gc() (rp1==rp2&&(rp2=(rp1=buf)+fread(buf,1,1<<22,stdin))==rp1?EOF:*rp1++)
char buf[1<<22],*rp1,*rp2;
 
inline int read(){
    int d=0,f=0;char ch=gc();
    while (!isdigit(ch)) f|=(ch=='-'),ch=gc();
    while (isdigit(ch)) d=d*10+ch-'0',ch=gc();
    return f?-d:d;
}
 
inline void write(int n){
    int stk[35],tp=0;
    do{
        stk[++tp]=n%10,n/=10;
    }while (n);
    while (tp) putchar(stk[tp--]+'0');
} 
 
const int N=20050,LG=16,K=31;
 
int n,q,a[N],lg[N];
struct Node {
    int x,y;
    Node operator+ (const Node &h)const {return x<h.x?h:*this;}
}st[LG][N],f[LG][N][K];
 
inline Node get(int l,int r){
    int t=lg[r-l+1];
    return st[t][l]+st[t][r-(1<<t)+1];
}
 
int pos[K];
 
int main(){
    n=read(),q=read();

    for (int i=2;i<=n;i++) lg[i]=lg[i>>1]+1;

    for (int i=1;i<=n;i++) a[i]=read(),st[0][i]={min(i+a[i],n),i};
 
    for (int i=1;(1<<i)<=n;i++)
        for (int j=1;j+(1<<i)-1<=n;j++)
            st[i][j]=st[i-1][j]+st[i-1][j+(1<<i-1)];
    
    for (int i=1;i<=n;i++){//预处理初始状态
        f[0][i][0]=get(i,min(i+a[i],n));
        for (int k=1;k<=30;k++) {
            Node lst=f[0][i][k-1];
            int r=min(a[i]+i+k,n);
            f[0][i][k]=(Node){min(lst.x+1,n),lst.y}+(Node){min(r+a[r],n),r};//加上一个多出的位置
        }
    }
    
    for (int i=1;i<=lg[n];i++)
        for (int j=1;j<=n;j++)
            for (int k=0;k<=30;k++)
                for (int x=0;x<=k;x++)
                    f[i][j][k]=f[i][j][k]+f[i-1][f[i-1][j][x].y][k-x];
    
    while (q--){
        int l=read(),r=read(),up=read(),ans=2;
        if (l==r) {puts("0");continue;}
        if (l+a[l]+up>=r) {puts("1");continue;}//特判
        for (int i=0;i<=up;i++) pos[i]=l;
        for (int i=lg[n];i>=0;i--){
            bool flag=1;
            for (int j=0;j<=up;j++) 
                if (f[i][pos[j]][up-j].x>=r) {flag=0;break;}//限制可及范围
            if (!flag) continue;
            ans+=(1<<i);
            for (int k=up;k>=0;k--){
                Node res={0,0};
                for (int x=0;x<=k;x++) res=res+f[i][pos[x]][k-x];//得到最优转移
                pos[k]=res.y;
            }
        }
        write(ans);putchar('\n');
    }
 
    return 0;
}
posted @ 2026-03-27 14:40  TP2010  阅读(6)  评论(0)    收藏  举报