单调栈、单调队列(模板)、子矩阵、连通块中点的数量、堆箱子(4星)

问题描述

给定一个长度为 NN 的序列 aa

第一行输出每个数字其左边 第一个比其 的数字,不存在则输出 -1

第二行输出每个数字其右边 第一个比其 的数字,不存在则输出 -1

第三行输出每个数字其左边 第一个比其 的数字,不存在则输出 -1

第四行输出每个数字其右边 第一个比其 的数字,不存在则输出 -1

update:本题数据于 2025-01-13 加强至 2×1052×105,以杜绝暴力通过。

输入格式

第一行输入一个正整数 NN 。(1≤N≤2×105)(1≤N≤2×105)

第二行输入 NN 个正整数,表示序列 aa 。(1≤ai≤105,1≤i≤N)(1≤a i ≤105,1≤iN)

输出格式

第一行输出每个数字其左边 第一个比其 的数字,不存在则输出 -1

第二行输出每个数字其右边 第一个比其 的数字,不存在则输出 -1

第三行输出每个数字其左边 第一个比其 的数字,不存在则输出 -1

第四行输出每个数字其右边 第一个比其 的数字,不存在则输出 -1

样例输入

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

样例输出

复制代码
`-1 4 3 2 -1
5 5 5 5 -1
-1 -1 -1 -1 1
3 2 1 -1 -1`
复制代码
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.lang.Thread.State;
import java.util.Stack;


public class Main {

    static int N = 2*100010,M=(int)(Math.log(N)/Math.log(2)+1);
    
  
    static int a[]=new int[N];
    static int result[]=new int[N];
    
    public static void main(String[] args) throws IOException {
    	
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        
        int n=Integer.parseInt(br.readLine());
        String g[] = br.readLine().split(" ");
        for (int i = 0; i < n; i++) {
			a[i]=Integer.parseInt(g[i]);
		}
        
//        第一行输出每个数字其左边第一个比其大的数字,不存在则输出 -1。
          //维护一个单调栈  存值对应的索引 值从栈顶到栈底递增 
          Stack<Integer> stack1=new Stack<>();
          
          for (int i = 0; i < n; i++) {
			int num=a[i];
			if(stack1.isEmpty()){
				result[i]=-1;
				
			}else{
				while(!stack1.isEmpty()){
					int ele=stack1.peek();
					if(a[ele]<=num){
						stack1.pop();
					}else{
						result[i]=a[ele];
						break;
					}
				}
				if(stack1.isEmpty()){
					result[i]=-1;
				}		
			}
			stack1.add(i);
		  }
          StringBuilder stringBuilder1=new StringBuilder();
          for (int i = 0; i < n; i++) {
			stringBuilder1.append(result[i]+" ");
		  }
          System.out.println(stringBuilder1.toString().trim());
//        第二行输出每个数字其右边第一个比其大的数字,不存在则输出 -1。
        Stack<Integer> stack2=new Stack<>();
        
        for (int i = n-1; i >=0 ; i--) {
			int num=a[i];
			if(stack2.isEmpty()){
				result[i]=-1;
				
			}else{
				while(!stack2.isEmpty()){
					int ele=stack2.peek();
					if(a[ele]<=num){
						stack2.pop();
					}else{
						result[i]=a[ele];
						break;
					}
				}
				if(stack2.isEmpty()){
					result[i]=-1;
				}		
			}
			stack2.add(i);
		  }
        StringBuilder stringBuilder2=new StringBuilder();
        for (int i = 0; i < n; i++) {
			stringBuilder2.append(result[i]+" ");
		  }
        System.out.println(stringBuilder2.toString().trim());
          
//        第三行输出每个数字其左边第一个比其小的数字,不存在则输出 -1。
          Stack<Integer> stack3=new Stack<>();        
        for (int i = 0; i < n ; i++) {
			int num=a[i];
			if(stack3.isEmpty()){
				result[i]=-1;
				
			}else{
				while(!stack3.isEmpty()){
					int ele=stack3.peek();
					if(a[ele]>=num){
						stack3.pop();
					}else{
						result[i]=a[ele];
						break;
					}
				}
				if(stack3.isEmpty()){
					result[i]=-1;
				}		
			}
			stack3.add(i);
		  }
        StringBuilder stringBuilder3=new StringBuilder();
        for (int i = 0; i < n; i++) {
			stringBuilder3.append(result[i]+" ");
		  }
        System.out.println(stringBuilder3.toString().trim());
        
//        第四行输出每个数字其右边第一个比其小的数字,不存在则输出 -1。
        Stack<Integer> stack4=new Stack<>();        
      for (int i = n-1; i >=0 ; i--) {
			int num=a[i];
			if(stack4.isEmpty()){
				result[i]=-1;
				
			}else{
				while(!stack4.isEmpty()){
					int ele=stack4.peek();
					if(a[ele]>=num){
						stack4.pop();
					}else{
						result[i]=a[ele];
						break;
					}
				}
				if(stack4.isEmpty()){
					result[i]=-1;
				}		
			}
			stack4.add(i);
		  }
      StringBuilder stringBuilder4=new StringBuilder();
      for (int i = 0; i < n; i++) {
			stringBuilder4.append(result[i]+" ");
		  }
      System.out.println(stringBuilder4.toString().trim());
    }
    
}

单调队列(模板)

问题描述

给定一个长度为 NN 的序列 aa 与一个长度为 KK 的窗口。(1≤K≤N)(1≤K≤N)

该窗口会从序列的最左端滑动到最右端,你需要输出 22 行,每行 N−K+1N−K+1 个数字。

第 11 行为每个窗口的最小值。

第 22 行为每个窗口的最大值。

输入格式

第一行输入两个正整数 N,KN,K。(1≤K≤N≤105)(1≤K≤N≤105)

第二行输入 NN 个正整数,表示序列 aa。(1≤ai≤105)(1≤ai​≤105)

输出格式

输出 22 行,每行 N−K+1N−K+1 个数字。

第 11 行为每个窗口的最小值。

第 22 行为每个窗口的最大值。

样例输入

复制代码
8 3
1 3 1 3 5 3 6 7

样例输出

复制代码
1 1 1 3 3 3 
3 3 5 5 6 7 

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {

    static int N = 100010;
    static int queue[]=new int[N];
  
    static int a[]=new int[N];
    
    
    public static void main(String[] args) throws IOException {
    	
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 
        String g[] = br.readLine().split(" ");
        int n=Integer.parseInt(g[0]),k=Integer.parseInt(g[1]);
        g = br.readLine().split(" ");
        for (int i = 0; i < n; i++) {
			a[i]=Integer.parseInt(g[i]);
		}
        
        int head=0,tail=0;
        //求最小值
        //维护一个单调递增的队列
        for (int i = 0; i < n; i++) {
			if(i-queue[head]>=k)head++;
			while(tail-head>0 && a[queue[tail-1]]>a[i])tail--;
			queue[tail++]=i;
			if(i>=k-1)System.out.print(a[queue[head]]+" ");
		}
        System.out.println();
        //求最大值
        //维护一个单调递减的队列
        head=0;tail=0;
        for (int i = 0; i < n; i++) {
			if(i-queue[head]>=k)head++;
			while(tail-head>0 && a[queue[tail-1]]<a[i])tail--;
			queue[tail++]=i;
			if(i>=k-1)System.out.print(a[queue[head]]+" ");
		}
        
    }
    
}

子矩阵

问题描述

给定一个 n×mn×m (nn 行 mm 列)的矩阵。设一个

矩阵的价值为其所有数中的最大值和最小值的乘积。求给定矩阵的所有大小为 a×ba×b (aa 行 bb 列)的子矩阵的价值的和。

答案可能很大,你只需要输出答案对 998244353998244353 取模后的结果。

输入格式

输入的第一行包含四个整数分别表示 nn,mm,aa,bb,相邻整数之间使用一个空格分隔。接下来

nn 行每行包含 mm 个整数,相邻整数之间使用一个空格分隔,表示矩阵中的每个数 Ai,jAi,j​。

输出格式

输出一行包含一个整数表示答案。

样例输入

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

样例输出

复制代码
58

样例说明

1×2+2×3+4×5+5×6=581×2+2×3+4×5+5×6=58。

评测用例规模与约定

对于 4040% 的评测用例,1≤n,m≤1001≤n,m≤100;

对于 7070% 的评测用例,1≤n,m≤5001≤n,m≤500;

对于所有评测用例,1≤a≤n≤10001≤a≤n≤1000,1≤b≤m≤10001≤b≤m≤1000,1≤Ai,j≤1091≤Ai,j​≤109。

复制代码
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;



public class Main {

    static int N = 1010,mod=998244353;
    static int computeMax[][]=new int[N][N];
    static int computeMin[][]=new int[N][N];
    static int matrixMax[][]=new int[N][N];
    static int matrixMin[][]=new int[N][N];
    static int num[][]=new int[N][N];
    
    
    public static void main(String[] args) throws IOException {
    
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 
        String g[] = br.readLine().split(" ");
        int n=Integer.parseInt(g[0]),m=Integer.parseInt(g[1]);
        int a=Integer.parseInt(g[2]),b=Integer.parseInt(g[3]);
        for (int i = 0; i < n; i++) {
        	g = br.readLine().split(" ");
			for (int j = 0; j < m; j++) {
				num[i][j] =Integer.parseInt(g[j]);
			}
		}
        for (int i = 0; i < n; i++) {
			clacMax(i,num[i],m,b);
			clacMin(i,num[i],m,b);
		}
       int res=0;
       for (int i = b-1; i < m; i++) {
		  
			 calcMax(i,n,a);
			 calcMin(i,n,a);
			 
		  
	   }
       for (int i = a-1; i < n; i++) {
		for (int j = b-1; j < m; j++) {
			res+=(int)(((long)matrixMax[i][j]*matrixMin[i][j])%mod);
		}
	   }
       System.out.println(res);
    }
    static void  calcMax(int col,int n,int k){
    	int q[]=new int[n];
    	
    	int head=0,tail=0;
    	for (int i = 0; i < n; i++) {
			if(i-q[head]>=k)head++;
			while(tail-head>0 && computeMax[q[tail-1]][col]<computeMax[i][col])tail--;
			q[tail++]=i;
			if(i>=k-1){
				matrixMax[i][col]=computeMax[q[head]][col];
				
			}
		}
    	
    }
    static void calcMin(int col,int n,int k){
    	int q[]=new int[n];
    	
    	int head=0,tail=0;
    	for (int i = 0; i < n; i++) {
			if(i-q[head]>=k)head++;
			while(tail-head>0 && computeMin[q[tail-1]][col]>computeMin[i][col])tail--;
			q[tail++]=i;
			if(i>=k-1){
				matrixMin[i][col]=computeMin[q[head]][col];
				
			}
		}
    	
    }
    static void clacMax(int row,int a[],int n,int k){
    	int q[]=new int[n];
    	int head=0,tail=0;
    	for (int i = 0; i < n; i++) {
			if(i-q[head]>=k)head++;
			while(tail-head>0 && a[q[tail-1]]<a[i])tail--;
			q[tail++]=i;
			if(i>=k-1)computeMax[row][i]=a[q[head]];
		}
    }
    static void clacMin(int row,int a[],int n,int k){
    	int q[]=new int[n];
    	int head=0,tail=0;
    	for (int i = 0; i < n; i++) {
			if(i-q[head]>=k)head++;
			while(tail-head>0 && a[q[tail-1]]>a[i])tail--;
			q[tail++]=i;
			if(i>=k-1)computeMin[row][i]=a[q[head]];
		}
    }
}

连通块中点的数量

问题描述

给定一个初始包含 nn 个点,00 条边的图。第 ii 个点的编号为 ii。(1≤i≤n)(1≤i≤n)

现在要进行 mm 次操作,操作共三种:

  • C a b:在点 aa 与点 bb 之间连一条边,a,ba,b 可能相等。
  • Q1 a b:询问点 aa 与点 bb 是否在一个连通块中,a,ba,b 可能相等。
  • Q2 a:询问点 aa 所在连通块中点的数量。

输入格式

第一行输入两个正整数 n,mn,m。(1≤n,m≤105)(1≤n,m≤105)

接下来 mm 行,每行输入包含一种操作。(1≤a,b≤n)(1≤a,b≤n)

输出格式

对于查询 Q1 a b:若 a,ba,b 在一个连通块中,输出 Yes,否则输出 No

对于查询 Q2 a:输出点 aa 所在连通块中点的数量。

样例输入

复制代码
5 7
C 1 2
Q1 1 2
Q2 1
C 2 5
Q2 5
Q1 1 1
Q1 2 3

样例输出

复制代码
Yes
2
3
Yes
No



import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;



public class Main {

    static int N = 100010;
    static int p[]=new int[N];
    static int sum[]=new int[N];
    public static void main(String[] args) throws IOException {
    
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 
        String g[] = br.readLine().split(" ");
        int n=Integer.parseInt(g[0]),m=Integer.parseInt(g[1]);
        for (int i = 1; i <= n; i++) {
			p[i]=i;sum[i]=1;
		}
//        C a b:在点 a 与点 b 之间连一条边,a,b 可能相等。
//        Q1 a b:询问点 a 与点 b 是否在一个连通块中,a,b 可能相等。
//        Q2 a:询问点 a 所在连通块中点的数量。
        for (int i = 0; i < m; i++) {
        	g = br.readLine().split(" ");
        	if(g.length==3){
        		int a=Integer.parseInt(g[1]),b=Integer.parseInt(g[2]);
        		if("C".equals(g[0])){
        			
        			if(a==b)continue;
        			union(a,b);
        		}else{
        			if(a==b){
        				System.out.println("Yes");continue;
        			}else{
        				if(find(a)==find(b))System.out.println("Yes");
        				else System.out.println("No");
        			}
        		}
        	}else{
        		int a=Integer.parseInt(g[1]);
        		a=find(a);
        		System.out.println(sum[a]);
        	}
		}
    }
    static int find(int x){
    	if(x!=p[x]){
    		p[x]=find(p[x]);
    		
    	}
    	return p[x];
    }
    static void union(int a,int b){
    	a=find(a);b=find(b);
    	if(a==b)return;//这句必须写
    	p[a]=b;
    	sum[b]+=sum[a];//只需要维护根节点的sum就可以 
    }
}

堆箱子(4星)

问题描述

暑假到了,小蓝打完蓝桥杯去工厂兼职,成为了工厂里的搬运工,工厂里面初始有 nn 个箱子平铺在地面上,每个箱子都有一个编号,分别是 1,2,3...n1,2,3...n。由于小蓝喜欢算法竞赛,他想到了一个问题。

现在有 22 种操作,具体操作如下:

  1. 1 a b,11 号操作,将 aa 位置所在的所有箱子搬到 bb 位置所在的箱子上面,已经在相同位置的箱子不能进行此操作。
  2. 2 a ,22 号操作,查询 aa 位置下有多少个箱子。

现在小蓝有 qq 次操作,对于每次操作 22 ,你需要输出结果。

输入格式

第一行二个整数 n,qn,q ,表示箱子的数量和操作次数。

接下来 qq 行,每行输入代表一个具体操作。

输出格式

对所有的操作 22 ,输出其结果。

样例输入

复制代码
6 6
1 4 2
2 2
2 4
1 2 3
2 2
2 4

样例输出

复制代码
0
1
1
2



import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;



public class Main {

    static int N = 3*10010;
    static int p[]=new int[N];
    static int sum[]=new int[N];
    static int d[]=new int[N];//i号箱子下面有多少箱子
    public static void main(String[] args) throws IOException {
    
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 
        String g[] = br.readLine().split(" ");
        int n=Integer.parseInt(g[0]),q=Integer.parseInt(g[1]);
        for (int i = 1; i <= n; i++) {
			p[i]=i;sum[i]=1;
		}

        for (int i = 0; i < q; i++) {
        	g = br.readLine().split(" ");
//        	操作 1 a b
//        	将a所在堆的全部箱子整体搬到b所在堆的顶部;
//        	约束条件:若a和b原本就在同一堆,则本次操作不执行。
//        	操作 2 a
//        	查询箱子a的下方一共有多少个箱子。
        	if(g.length==3){
        		int a=Integer.parseInt(g[1]),b=Integer.parseInt(g[2]);
        		if(a==b)continue;
        		else{
        			union(a, b);
        		}
        	}else{
        		int a=Integer.parseInt(g[1]);
        		
        		find(a);
        		System.out.println(d[a]);
        	}
		}
    }
    static int find(int x){
    	if(x!=p[x]){
    		//两行不能变
    		int root=find(p[x]);
    		d[x]+=d[p[x]];//一定要先完成路径压缩
    		p[x]=root;
    	}
    	return p[x];
    }
    static void union(int a,int b){
    	a=find(a);b=find(b);
    	if(a==b)return;
    	p[a]=b;
    	
    	//两行顺序不能变
    	d[a]+=sum[b];
    	sum[b]+=sum[a];//只需要维护根节点的sum就可以 
    	
    }
}
相关推荐
通信小呆呆1 小时前
Vandermonde结构及其快速算法详解
线性代数·算法
云泽8082 小时前
笔试算法 - 链表篇(一):移除、反转、合并、回文判断全解析
数据结构·c++·算法·链表
菜菜的顾清寒2 小时前
HOT力扣100(43)二叉树-翻转二叉树
数据结构·算法·leetcode
通信小呆呆2 小时前
Toeplitz结构及其快速算法详解
算法
YikNjy2 小时前
break和continue
java·开发语言·算法
春日见2 小时前
五分钟入门 强化学习---DQN(Deep Q Net)算法与实现
人工智能·python·深度学习·算法·microsoft·机器学习
budingxiaomoli3 小时前
动态规划--斐波那契数列模型
算法·动态规划
IT猿手3 小时前
多目标优化算法:多目标蛇优化算法(Multiple Objective Snake Optimizer,MOSO)(提供MATLAB代码)
开发语言·算法·matlab·动态路径规划·光伏模型参数估计