CF1523H Hopping Around the Array

CF1523H Hopping Around the Array

这就是tourist等一众大佬没做出来的题吗??

Lemma

个人感觉的题眼所在
由于蚱蜢的弹跳始终向右,一个被删掉的点只会被越过一次
所以可以将删点操作转化为一次跳跃可以多跳一个

Solve

对于没有删点操作,显然可以用倍增实现快速跳跃
考虑到数据范围极小,可以直接将操作次数k直接作为一个维度存入倍增数组
每次跳跃时对前后两个k进行合并
复杂度\(O(qk^2log_2n+nk^2log_2n)\)

代码环节

写法有点恶臭,有些小细节见代码注释

#include<bits/stdc++.h>
using namespace std;

#define Mod(x) (x>=P)&&(x-=P)
#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)
#define erep(i,a) for(int i=hd[a];i;i=nxt[i])

typedef long long ll;
void Max(int &x,int y){(x<y)&&(x=y);}
void Min(int &x,int y){(x>y)&&(x=y);}

bool vio;
char IO;
int rd(int res=0){
	bool f=0;
	while(IO=getchar(),IO<48||IO>57)
		f=IO=='-';
	do res=(res<<1)+(res<<3)+(IO^48);
	while(IO=getchar(),isdigit(IO));
	return f?-res:res;
}
const int M=20005;
int fa[M][21][31],n,q,A[M],p[33];
int Mx(int &x,int y){return x+A[x]>y+A[y]?x:y;}
struct ST{
	int f[22][M],lg[M];
	void build(){
		rep(i,2,n)lg[i]=lg[i>>1]+1;
		rep(i,1,n)f[0][i]=i;
		rep(j,1,20)rep(i,1,n)
			f[j][i]=Mx(f[j-1][i],f[j-1][i+(1<<(j-1))]);
	}
	int qry(int l,int r){
		int t=lg[r-l+1];
		return Mx(f[t][l],f[t][r-(1<<t)+1]);
	}
}T;
bool let;
int main(){
	cerr<<(&vio-&let)/1024.0/1024<<endl;
	n=rd(),q=rd();
	rep(i,1,n)A[i]=min(n-i,rd());
	T.build();
	rep(i,1,n){
		fa[i][0][0]=T.qry(i,i+A[i]);
		rep(j,1,30)fa[i][0][j]=min(i+j+A[i],n);
		//使用加长跳跃操作时必然是为了恰好跳到一个更优的位置
		//否则可以节省操作
	}
	rep(j,1,20)rep(k,0,30)rep(i,1,n)rep(t,0,k)
		fa[i][j][k]=Mx(fa[i][j][k],fa[fa[i][j-1][t]][j-1][k-t]);
	while(q--){
		int l=rd(),r=rd(),t=rd();
		if(l==r){puts("0");continue;}
		if(l+t+A[l]>=r){puts("1");continue;}
		//能直接跳至终点时就直接终点,无需贪心控制范围最远
		//以下同理
		int ans=2;
		rep(i,0,30)p[i]=l;
		drep(i,20,0){
			bool fl=1;
			rep(j,0,30)rep(k,0,t-j)
				if(fa[p[k]][i][j]+A[fa[p[k]][i][j]]+t-j-k>=r){fl=0;break;}
			if(!fl)continue;
			ans+=1<<i;
			drep(j,30,0)drep(k,t-j,0)
				p[k+j]=Mx(p[k+j],fa[p[j]][i][k]);
		}
		printf("%d\n",ans);
	}	
	return 0;
}
posted @ 2021-06-06 09:52  Saltywater  阅读(88)  评论(0)    收藏  举报