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\) 位置。
-
对于 \(i\le q<p\) 的位置 \(q\)。显然,除开 \(p,q\) 的可及范围的重叠部分,剩下的只在 \(q\) 中的部分在 \(p\) 之前,可及范围仍然没有 \(p\) 远。
-
对于 \(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\),类似背包。
转移方程(注意重载了运算符):
即:
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;
}

浙公网安备 33010602011771号