首页 > 其他分享 >单调栈

单调栈

时间:2024-11-24 12:55:00浏览次数:6  
标签:下标 int 元素 leq 异或 单调

【模板】单调栈

题目描述

给出项数为 \(n\) 的整数数列 \(a_{1 \dots n}\)。

定义函数 \(f(i)\) 代表数列中第 \(i\) 个元素之后第一个大于 \(a_i\) 的元素的下标,即 \(f(i)=\min_{i<j\leq n, a_j > a_i} \{j\}\)。若不存在,则 \(f(i)=0\)。

试求出 \(f(1\dots n)\)。

输入格式

第一行一个正整数 \(n\)。

第二行 \(n\) 个正整数 \(a_{1\dots n}\)。

输出格式

一行 \(n\) 个整数表示 \(f(1), f(2), \dots, f(n)\) 的值。

样例

样例输入

5
1 4 2 3 5

样例输出

2 5 4 5 0

提示

【数据规模与约定】

对于 \(30\%\) 的数据,\(n\leq 100\);

对于 \(60\%\) 的数据,\(n\leq 5 \times 10^3\) ;

对于 \(100\%\) 的数据,\(1 \le n\leq 3\times 10^6\),\(1\leq a_i\leq 10^9\)。

[!NOTE]

单调栈原理:把无用数据及时去除(在需要快速找到元素的相对关系(如左右边界、最大/最小值)时特别有用)

典型的使用场景:下一个更大元素问题、区间最值问题、直方图最大矩形问题和滑动窗口问题

单调递增栈:只有比栈顶元素小的元素才能直接进栈,否则需要先将栈中比当前元素小的元素出栈,再将当前元素入栈

栈中保留的都是比当前入栈元素大的值,并且从栈顶到栈底的元素值是单调递增的

单调递减栈:只有比栈顶元素大的元素才能直接进栈,否则需要先将栈中比当前元素大的元素出栈,再将当前元素入栈。

栈中保留的都是比当前入栈元素小的值,并且从栈顶到栈底的元素值是单调递减的

[!TIP]

从后往前扫

对于每个点:

①弹出栈顶比其更小的元素

②此时栈顶就是答案

③加入这个元素

#include<cstdio>
#include<stack>
using namespace std;
const int N = 3e6 + 10;
int n, a[N], f[N];//a是需要判断的数组,f是存储答案的数组
stack<int>baka;//创建空栈s来存储数组a中的索引值
int main()
{
	scanf("%d", &n);
    
	for (int i = 1; i <= n; i++) 
	scanf("%d", &a[i]);

	for (int i = n; i >= 1; i--)//从数组的末尾开始向前遍历确保在处理某个元素时,栈中存储的是该元素右侧的所有元素的索引
	{
		while (!baka.empty()&&a[baka.top()]<=a[i])
            baka.pop();//栈顶元素存在并大于当前元素,栈顶元素出栈(移除栈中所有小于或等于当前元素a[i]的索引,这些元素不可能成为a[i]右侧的第一个更大元素)
		if (baka.empty()) 
             f[i] = 0;//栈为空,说明当前元素a[i]右侧没有更大的元素,则将f[i]设置为0
		else f[i] = baka.top();//否则栈顶的元素就是a[i]右侧第一个比它大的元素的索引(赋值给f[i])

		baka.push(i);//当前元素入栈
	}
	for (int i = 1; i <= n; i++) 
	printf("%d ", f[i]);
	return 0;
}

求数列所有后缀最大值的位置

题目描述

给定一个数列 \(a\),初始为空。有 \(n\) 次操作,每次在 \(a\) 的末尾添加一个正整数 \(x\)

每次操作结束后,请你找到当前 \(a\) 所有的后缀最大值的下标(下标从 1 开始)。一个下标 \(i\) 是当前 \(a\) 的后缀最大值下标当且仅当:对于所有的 \(i < j \leq |a|\),都有 \(a_i > a_j\),其中 \(|a|\) 表示当前 \(a\) 的元素个数

为了避免输出过大,请你每次操作结束后都输出一个整数,表示当前数列所有后缀最大值的下标的按位异或和

输入格式

第一行是一个整数,表示操作次数 \(n\)
第二行有 \(n\) 个整数,依次表示 \(n\) 次操作所添加的整数 \(x_i\)

输出格式

每次操作后请输出一行一个整数,表示当前数列所有后缀最大值下标的按位异或和

样例

样例输入

5
2 1 3 5 4

样例输出

1
3
3
4
1

提示

数据规模与约定

对于全部的测试点,保证 \(1 \leq n \leq 10^6\),\(1 \leq x_i \lt 2^{64}\)(好大)

[!TIP]

数列a按顺序入单调栈,单调栈里的元素从大到小排序,计算每次每个元素入栈时,栈内元素下标的异或和

模板是找单调递增的栈,本题是找单调递减的栈(栈中每个数字的后缀(第几个出现的数)按位异或^和

单调栈中是当前的后缀最大值,当有数据被移出或进入时,将答案与其下标做异或运算

[!NOTE]

按位异或运算符“^”是双目运算符,其功能是参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1,参与运算数仍以补码出现

例如 9^5可写成算式如下: 00001001^00000101 00001100 (十进制为12)

异或运算最重要的性质就是自反性(a^b) ^ b=a

  • x ^ x = 0:任何数与自身异或结果为0
  • x ^ 0 = x:任何数与0异或结果为自身
#include <iostream>
#include <stack>
using namespace std;
typedef unsigned long long qwq;
const int N = 1e6 + 10;
qwq a[N];  // 存储输入值
int n, baka = 0; // 存储输入值的下标异或和
stack<int> b; //单调栈存储下标

int main() 
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) 
    {
        scanf("%llu", &a[i]);

        while (!b.empty() && a[b.top()] <= a[i]) //移除栈中所有<=当a[i] 的下标
        {
            baka ^= b.top(); // 消除出栈元素影响(出栈前将栈顶元素的下标与baka异或)
            b.pop();//栈顶元素出栈
        }

        b.push(i);//入栈
        baka ^= b.top();//更新

        printf("%d\n", baka);
    }

    return 0;
}



标签:下标,int,元素,leq,异或,单调
From: https://www.cnblogs.com/gailixia/p/18565683

相关文章

  • 【模板】单调栈
    题目背景模板题,无背景。2019.12.12更新数据,放宽时限,现在不再卡常了。题目描述给出项数为n的整数数列a1...n。定义函数f(i)代表数列中第i个元素之后第一个大于ai的元素的下标,即。若不存在,则f(i)=0。试求出f(1...n)。输入格式第一行一个正整数n。第二行n......
  • 单调栈
    一.【模板】单调栈题目描述给出项数为\(n\)的整数数列\(a_{1\dotsn}\)。定义函数\(f(i)\)代表数列中第\(i\)个元素之后第一个大于\(a_i\)的元素的下标,即\(f(i)=\min_{i<j\leqn,a_j>a_i}\{j\}\)。若不存在,则\(f(i)=0\)。试求出\(f(1\dotsn)\)。输入格式......
  • 递推数列的极限(上)------单调有界部分
    不管怎么样,求极限之前都要先证明极限存在,即数列收敛。证明数列收敛两种方法:一种是单调有界准则,一种是夹逼准则。一.单调有界准则例1上面这道题的心路历程:先在草稿纸用上帝视角求出‘极限’,虽然是猜的,但是一定是对的。然后根据这个极限,以及题目给的条件,比如这道题给......
  • 算法思想——单调栈及其运用实例
    单调栈的定义单调栈是一种数据结构,它维护了一个元素序列,这个序列在栈内要么单调递增,要么单调递减。在单调栈中,新元素的插入操作会遵循特定的规则:对于单调递增栈,只有当新元素大于或等于栈顶元素时,才能将其压入栈中;对于单调递减栈,则相反,只有当新元素小于或等于栈顶元素时,才......
  • C++ 算法学习——1.8 单调队列算法
    单调队列(MonotonicQueue)是一种特殊类型的队列,通常用于解决一些数组或序列相关的问题。和单调栈类似,单调队列也具有一些特定的性质,在解决一些问题时非常有用。以下是关于单调队列的一些重要点:定义:单调队列是一种数据结构,队列中的元素满足单调递增或单调递减的性质。应用:单......
  • 单调队列优化DP解题报告(uoj转)
    Fence\(K\)很小,考虑\(K\)开一维,\(N\)开一维\(f_{i,j}\)表示前\(i\)个工匠粉刷前\(j\)个木板的最大花费\(f_{i,j}=\min_{k=j-l_i}^{s_i-1}f_{i-1,k}+(j-k)\cdotp_i\)拆开为\(f_{i,j}=f_{i-1,k}-k\cdotp_i+j\cdotp_i\)\(i\)固定时维护\(f_{i-1,k}-k\cdotp_i\)的最小......
  • 滑动窗口+单调队列
    题目:2398.预算内的最多机器人数目答案:#fromtypingimportList#fromcollectionsimportdequeclassSolution:defmaximumRobots(self,chargeTimes:List[int],runningCosts:List[int],budget:int)->int:res,n,runningCostSum=0,len(chargeTi......
  • 单调队列优化 DP
    单调队列优化DP回忆单调队列的作用,\(O(n)\)求出每一个大小为\(K\)的窗口中的最大、最小值。以最大值为例,我们可以得到如下DP转移方程:\[dp[i]=\max(val[j])+base[i],i-j\leqK\]其中\(base[i]\)是一个仅与\(i\)有关的式子,不受\(j\)影响,且可以预处理得到;而\(val[j]......
  • 单调队列优化 dp
    1.概念单调队列优化的本质是借助单调性,及时排除不可能的决策,保持候选集合的秩序性。2.例题P1714切蛋糕题目大意:给定一个序列,找出长度不超过\(m\)的连续子序列,使得子序列中所有数的和最大。思路:要求区间和,首先求出前缀和,然后考虑朴素dp,不难想到用\(dp[i]\)表示包含......
  • 洛谷题单指南-常见优化技巧-P1886 滑动窗口 /【模板】单调队列
    原题链接:https://www.luogu.com.cn/problem/P1886题意解读:单调队列模版题。解题思路:采用双端队列维护单调的序列,单调队列三部曲:1、去头,当窗口内元素个数超过k,队头出队2、去尾,当要加入的元素会破坏单调性,队尾出队3、入队,将元素的下标存入队列每一次入队后,队头元素即为窗口最......