问题描述
给定一个长度为 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≤i ≤N)
输出格式
第一行输出每个数字其左边 第一个比其大 的数字,不存在则输出 -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 a b,11 号操作,将 aa 位置所在的所有箱子搬到 bb 位置所在的箱子上面,已经在相同位置的箱子不能进行此操作。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就可以
}
}
