• Codeforces 1523H. Hopping Around the Array 题解


    题目链接:H. Hopping Around the Array

    题目大意:给定一个序列 (a_1,a_2,dots,a_n),在第 (i) 个点可以一步走到 ([i,i+a_i]) 中的任何一个点,多次询问,每一次从 (l) 出发,你可以删掉不超过 (k) 的点,将剩下的点重标号,使得到达 (r) 的步数最少。
    (nleq 2 imes 10^4)(kleq 30)


    题解:首先考虑没有删除点应当怎么做,显然我们从第 (i) 个点出发,我们可以到达的点是 ([i,i+a_i]),那么我们假设我们下一步走到的点为 (j(jin [i,i+a_i])),那么 (j) 一定满足 (forall ileq kleq i+a_i,j+a_jgeq k+a_k)

    如果我们删点,那么假设在第 (i) 个点,删除了 (x) 个点,那么显然会走到 (i+a_i+x),否则的话就没有必要删这么多。

    那么我们就很容易设计一个状态,设 (f_{i,k,j}) 表示从 (j) 出发,走 (2^i) 步,可以删除不超过 (k) 个点可以走到的最远点。

    然后转移的时候我们直接暴力枚举 (k_1,k_2)(max_{l=j}^{f_{i,k_1,j}} f_{i,k_2,l}) 的答案贡献到 (f_{i+1,k_1+k_2,j}) 上,这部分可以通过数据结构来优化。

    接下来把所有的询问离线一起处理,设 (ans_{t,i,j}) 表示第 (i) 个询问,删除 (j) 个点,在考虑 (geq 2^t) 步的所有的情况下所能够到达的最靠右的不超过 (r_i) 的点是什么(由我们在题解开头给出的结论,容易得到这样贪心是正确的),初始的时候是 (forall 0leq jleq k_i, ans_{log n,i,j}=l_i)

    然后这一部分中间的转移我们同样可以通过数据结构优化。

    时间复杂度为:(O(nklog^2 n + nk^2log n))

    代码:

    #include <cstdio>
    #include <algorithm>
    const int Maxn=20000;
    const int Maxd=17;
    const int Maxk=30;
    int n,q;
    int nxt[Maxd+1][Maxk+1][Maxn+1];
    int f_max[Maxk+1][Maxd+1][Maxn+1];
    int a[Maxn+1];
    int log_2[Maxn+1];
    void init(){
    	log_2[0]=-1;
    	for(int i=1;i<=Maxn;i++){
    		log_2[i]=log_2[i>>1]+1;
    	}
    }
    int query_max(int k,int l,int r){
    	int d=log_2[r-l+1];
    	return std::max(f_max[k][d][l],f_max[k][d][r-(1<<d)+1]);
    }
    int ans[Maxn+1];
    int pos_r[Maxn+1][Maxk+1];
    struct Question{
    	int l,r,k;
    }qu[Maxn+1];
    int main(){
    	init();
    	scanf("%d%d",&n,&q);
    	for(int i=1;i<=n;i++){
    		scanf("%d",&a[i]);
    	}
    	for(int i=1;i<=n;i++){
    		for(int j=0;j<=Maxk;j++){
    			nxt[0][j][i]=std::min(i+a[i]+j,n);
    		}
    	}
    	for(int i=1;i<=17;i++){
    		for(int j=1;j<=n;j++){
    			for(int k=0;k<=Maxk;k++){
    				f_max[k][0][j]=nxt[i-1][k][j];
    			}
    		}
    		for(int k=0;k<=Maxk;k++){
    			for(int j=1;(1<<j)<=n;j++){
    				for(int l=1;l+(1<<j)-1<=n;l++){
    					f_max[k][j][l]=std::max(f_max[k][j-1][l],f_max[k][j-1][l+(1<<(j-1))]);
    				}
    			}
    		}
    		for(int k_1=0;k_1<=Maxk;k_1++){
    			for(int k_2=0;k_1+k_2<=Maxk;k_2++){
    				for(int j=1;j<=n;j++){
    					nxt[i][k_1+k_2][j]=std::max(nxt[i][k_1+k_2][j],query_max(k_2,j,nxt[i-1][k_1][j]));
    				}
    			}
    		}
    	}
    	for(int i=1;i<=q;i++){
    		scanf("%d%d%d",&qu[i].l,&qu[i].r,&qu[i].k);
    		for(int j=0;j<=qu[i].k;j++){
    			pos_r[i][j]=qu[i].l;
    		}
    		if(qu[i].l==qu[i].r){
    			ans[i]=-1;
    			continue;
    		}
    	}
    	for(int i=17;i>=0;i--){
    		for(int j=1;j<=n;j++){
    			for(int k=0;k<=Maxk;k++){
    				f_max[k][0][j]=nxt[i][k][j];
    			}
    		}
    		for(int k=0;k<=Maxk;k++){
    			for(int j=1;(1<<j)<=n;j++){
    				for(int l=1;l+(1<<j)-1<=n;l++){
    					f_max[k][j][l]=std::max(f_max[k][j-1][l],f_max[k][j-1][l+(1<<(j-1))]);
    				}
    			}
    		}
    		for(int j=1;j<=q;j++){
    			if(ans[j]==-1){
    				continue;
    			}
    			static int np_r[Maxk+5];
    			for(int k=0;k<=qu[j].k;k++){
    				np_r[k]=0;
    			}
    			for(int k_1=0;k_1<=qu[j].k;k_1++){
    				for(int k_2=0;k_1+k_2<=qu[j].k;k_2++){
    					np_r[k_1+k_2]=std::max(np_r[k_1+k_2],query_max(k_2,qu[j].l,pos_r[j][k_1]));
    				}
    			}
    			if(np_r[qu[j].k]<qu[j].r){
    				ans[j]+=(1<<i);
    				for(int k=0;k<=qu[j].k;k++){
    					pos_r[j][k]=np_r[k];
    				}
    			}
    		}
    	}
    	for(int i=1;i<=q;i++){
    		printf("%d
    ",ans[i]+1);
    	}
    	return 0;
    }
    
  • 相关阅读:
    html——黑体、斜体、下划线及删除线
    <转>DataGridView分页控件
    (转)Log4J 最佳实践之全能配置文件
    C# 单例代码
    MySql基本语句
    .NET中windows服务如何获取自己的安装路径
    web开发网址收藏...
    将字符串转换为json对象_正确语法
    (转)理解矩阵一、二、三
    将数据写入EXCEL多个表
  • 原文地址:https://www.cnblogs.com/withhope/p/14835749.html
Copyright © 2020-2023  润新知