单调栈+倍增思想 皇家守卫【算法赛】、单调队列 附近最小

皇家守卫【算法赛】

问题描述

在蓝桥王国的边疆有 NN​ 座高塔,每座高塔上均有一个士兵,他们被称之为皇家守卫。

每座高塔均有一个高度,第 ii 座高塔的高度为 hihi​。对于第 ii 座塔,若其右边存在某座塔 jj,满足:max⁡(hi,hi+1,hi+2,⋯,hj−1)<hjmax(hi​,hi+1​,hi+2​,⋯,hj−1​)<hj​则称第 jj 座塔为第 ii 座塔 "暸望塔"。

现在王国传来 QQ 个任务,每个任务会给定两个编号 x,yx,y,需要你求解第 xx 座高塔和 第 yy 座高塔的公共 "暸望塔" 数量。

x,yx,y 的公共 "暸望塔" 定义为两者同时拥有的 "暸望塔"。

输入格式

第一行输入两个整数 N,Q(1≤N,Q≤105)N,Q(1≤N,Q≤105) 表示高塔个数和任务数。

第二行输入 NN 个整数 h1,h2,h3,⋯,hn(1≤hi≤109)h1​,h2​,h3​,⋯,hn​(1≤hi​≤109) 表示高塔的高度。

接下来 QQ 行,每行输入两个整数 x,y(1≤x≤y≤N)x,y(1≤x≤y≤N) 表示一次任务询问。

输出格式

输出 QQ 行,每行一个整数表示答案

输入样例

复制代码
5 3
1 3 4 5 7
1 3
2 4
3 5

输出样例

复制代码
2
1
0

说明

对于第一个询问,第 11 座高塔和第 33 座高塔的公共暸望塔有 4,54,5 号塔。

复制代码
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.*;


public class Main {

    static int N=100010;
    
    
    static int a[]=new int[N];
    static int next[]=new int[N];//表示右边第一个比我高的高塔的索引
    static int len[]=new int[N];//表示 右边严格大于我的高塔的数量
    static int log2[]=new int[N];
    static int go[][];
    static int n;
    
    
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 
    static BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out));
    
    public static void main(String[] args) throws IOException { 
        StringTokenizer st=new StringTokenizer(br.readLine());
        n=Integer.parseInt(st.nextToken());
        int q=Integer.parseInt(st.nextToken());
       
        st=new StringTokenizer(br.readLine());
        for (int i = 1; i <= n; i++) {
        	a[i]=Integer.parseInt(st.nextToken());
		}
        a[n+1]=Integer.MAX_VALUE;
        findMax(n);
        for (int i = n-1; i >0 ; i--) {//从后往前遍历
			if(next[i]!=n+1)len[i]=len[next[i]]+1;//要加判断语句
		}
        
        log2[1]=0;
        for (int i = 2; i < N; i++) {
			log2[i]=log2[i>>1]+1;
		}
        
        go=new int[n+2][log2[n]+1];//go(i,j)表示从i出发跳2^j步得到的下标
        for (int j = 0; j <= log2[n]; j++) {
			for (int i = 1; i <= n; i++) {
				if(j==0)go[i][j]=next[i];
				else {
					go[i][j]=go[go[i][j-1]][j-1];
				}
			}
		}
        
        for (int i = 0; i < q; i++) {
        	st=new StringTokenizer(br.readLine());
        	int x=Integer.parseInt(st.nextToken()),y=Integer.parseInt(st.nextToken());
        	
        	//题目可以理解为求x位置的瞭望塔中索引大于y塔的数量  是x的瞭望塔一定是y的
        	//我们可以利用倍增思想去遍历塔
        	int nx=x;
        	for (int k = log2[n]; k >=0; k--) {//这里一定要从最大步长不断递减  
          //当前位置能跳的最大幅度,再微调,能走到极限  
          //先挪一小步,把原本能跳的大步机会直接废掉,走不到最远
        		int nxt=go[nx][k];
        		if(nxt<=y && nxt!=0){
        			nx=nxt;       			
        		}
			}
        	
        	bw.write(len[nx]+"\n");
		}
        
        
        br.close();
        bw.flush();
        bw.close();      
    }
    static void findMax(int n){//维护next
    	for (int i = 1; i <= n; i++) {
			next[i]=n+1;
		}
    	Stack<Integer> stack=new Stack<>();//维护一个单调递减栈
    	for (int i = n; i >=1; i--) {
			int u=a[i];
			while(!stack.isEmpty() && a[stack.peek()]<=u)stack.pop();
			if(!stack.isEmpty())next[i]=stack.peek();
			stack.add(i);
		}
    	
    }
}

附近最小

问题描述

小蓝有一个序列 a1,a2,...,ana1,a2,...,an

给定一个正整数 kk,请问对于每一个 11 到 nn 之间的序号 ii,ai−k,ai−k+1,...,ai+kai−k,ai−k+1,...,ai+k 这 2k+12k+1 个数中的最小值是多少?

当某个下标超过 11 到 nn 的范围时,数不存在,求最小值时只取存在的那些值。

输入格式

输入的第一行包含一整数 nn。

第二行包含 nn 个整数,分别表示 a1,a2,...,ana1,a2,...,an

第三行包含一个整数 kk 。

输出格式

输出一行,包含 nn 个整数,分别表示对于每个序号求得的最小值。

样例输入

复制代码
5 
5 2 7 4 3 
1 

样例输出

复制代码
2 2 2 3 3 

评测用例规模与约定

对于 30% 的评测用例,1<=n<=1000,1<=ai<=10001<=n<=1000,1<=ai<=1000。

对于 50% 的评测用例,1<=n<=10000,1<=ai<=100001<=n<=10000,1<=ai<=10000。

对于所有评测用例,1<=n<=1000000,1<=ai<=10000001<=n<=1000000,1<=ai<=1000000。

法1(添加数据)

复制代码
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.*;


public class Main {

    static int N=1000010;
    
    
    static int a[]=new int[N];
    static int c[]=new int[2*N];
    static int res[]=new int[2*N];//表示右边第一个比我高的高塔的索引
    
    static int n;
    
    
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 
    static BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out));
    
    public static void main(String[] args) throws IOException { 
        StringTokenizer st=new StringTokenizer(br.readLine());
        n=Integer.parseInt(st.nextToken());
       
       
        st=new StringTokenizer(br.readLine());
        
        for (int i = 1; i <= n; i++) {
        	a[i]=Integer.parseInt(st.nextToken());
		}
        st=new StringTokenizer(br.readLine());
        int k=Integer.parseInt(st.nextToken());  
        int ii = 1;
        for (; ii <= k; ii++) {
			c[ii]=Integer.MAX_VALUE;
		}
        for (int j=1; j<=n; ii++,j++) {
			c[ii]=a[j];
		}
        for (int j=1; j<= k; j++,ii++) {
        	c[ii]=Integer.MAX_VALUE;
		}
        for (int i = 1; i < ii; i++) {
			res[i]=Integer.MAX_VALUE;
		}
        
        Deque<Integer> deque=new LinkedList<Integer>();//维护一个单调递增的队列
        
        for (int i = 1; i < ii; i++) {
        	int u=c[i];
        	//清除过期数据
			while(!deque.isEmpty() && i-deque.getFirst()>=2*k+1)deque.pollFirst();
			//维护队列
			while(!deque.isEmpty() && c[deque.peekLast()]>=u)deque.pollLast();
			deque.add(i);
		    res[i]=deque.peek();
		    
		}
        for (int i =2*k+1,j=1; j<=n; j++,i++) {//注意开始下标
			bw.write(c[res[i]]+" ");
		}
        
        br.close();
        bw.flush();
        bw.close();      
    }
   
}

法2(枚举中心点)

复制代码
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.*;


public class Main {

    static int N=1000010;
    
    
    static int a[]=new int[N];
    
    static int res[]=new int[N];//表示右边第一个比我高的高塔的索引
    
    static int n;
    
    
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 
    static BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out));
    
    public static void main(String[] args) throws IOException { 
        StringTokenizer st=new StringTokenizer(br.readLine());
        n=Integer.parseInt(st.nextToken());
       
       
        st=new StringTokenizer(br.readLine());
        
        for (int i = 1; i <= n; i++) {
        	a[i]=Integer.parseInt(st.nextToken());
		}
        st=new StringTokenizer(br.readLine());
        int k=Integer.parseInt(st.nextToken());  
       
        Deque<Integer> deque=new LinkedList<Integer>();//维护一个单调递增的队列
        int right=0;//当前已经处理过的右边界
        for (int i = 1; i <= n; i++) {//枚举每个中心点
        	int l=Math.max(1, i-k),r=Math.min(n, i+k);//保证合法索引
        	//清除过期数据
			if(!deque.isEmpty() && deque.getFirst()<l)deque.pollFirst();//每次左移一个单位 不用写成循环
			//把属于l到r范围的数据添加进去
			while(right<r){
				right++;
				while(!deque.isEmpty() && a[deque.peekLast()]>=a[right])deque.pollLast();
				deque.add(right);
			}
			
			res[i]=deque.peekFirst();
		}
        for (int i = 1; i <= n; i++) {
			bw.write(a[res[i]]+" ");
		}
        
        br.close();
        bw.flush();
        bw.close();      
    }
   
}
相关推荐
QN1幻化引擎1 小时前
给 AI 做一次「意识体检」——基于 QN1 幻化引擎的灵鉴意识识别框架与 DalinX V5 实测
大数据·数据结构·人工智能·算法·架构
拂拉氏2 小时前
【知识讲解】 AVL树从基本成员的介绍到核心接口的实现(插入、判断、删除等等)
数据结构·算法·avl树
可靠的仙人掌2 小时前
SAC(Soft Actor-Critic)算法底座
开发语言·算法·php
海石4 小时前
单调栈复健,顺便,牺牲一下吧,空间复杂度!一切献给AC
算法·leetcode
海石4 小时前
JS击败94%,Hard题想不到动态规划,那就用数组和栈试试
算法·leetcode
alphaTao6 小时前
LeetCode 每日一题 2026/7/6-2026/7/12
算法·leetcode
想吃火锅10056 小时前
【leetcode】56.合并区间js
算法·leetcode·职场和发展
imuliuliang6 小时前
可合并堆在多任务调度中的优势与实现技巧7
算法
学究天人6 小时前
数学公理体系大全:Comprehensive Collection of Mathematical Axiom Systems(卷6)
网络·算法·数学建模·动态规划·几何学·图论·拓扑学