原理就不讲了:小破站一搜一堆的,这里就放一些题目用于练习吧!参考连接也会附在相应位置!!!!
1、单调栈和单调队列
AcWing 154. 滑动窗口:单调队列
给定一个大小为
n
≤
1
0
6
n≤10^6
n≤106 的数组。
有一个大小为 k k k 的滑动窗口,它从数组的最左边移动到最右边。
你只能在窗口中看到 k k k 个数字。
每次滑动窗口向右移动一个位置。
以下是一个例子:
该数组为 [1 3 -1 -3 5 3 6 7]
,
k
k
k 为
3
3
3。
窗口位置 | 最小值 | 最大值 |
---|---|---|
[1 3 -1] -3 5 3 6 7 | -1 | 3 |
1 [3 -1 -3] 5 3 6 7 | -3 | 3 |
1 3 [-1 -3 5] 3 6 7 | -3 | 5 |
1 3 -1 [-3 5 3] 6 7 | -3 | 5 |
1 3 -1 -3 [5 3 6] 7 | 3 | 6 |
1 3 -1 -3 5 [3 6 7] | 3 | 7 |
你的任务是确定滑动窗口位于每个位置时,窗口中的最大值和最小值。
输入格式
输入包含两行。
第一行包含两个整数 n n n 和 k k k,分别代表数组长度和滑动窗口的长度。
第二行有 n n n 个整数,代表数组的具体数值。
同行数据之间用空格隔开。
输出格式
输出包含两个。
第一行输出,从左至右,每个位置滑动窗口中的最小值。
第二行输出,从左至右,每个位置滑动窗口中的最大值。
输入样例:
8 3
1 3 -1 -3 5 3 6 7
输出样例:
-1 -3 -3 -3 3 3
3 3 5 5 6 7
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+10;
int q[N],a[N];
int n,k;
int main(){
scanf("%d%d", &n, &k);
for(int i = 0 ;i <n ;i ++) scanf("%d", &a[i]);
int tt = -1,hh = 0;
for(int i =0 ;i < n; i++){
while(tt >=hh && a[q[tt]]>a[i]){
--tt;
}
q[++tt] = i;
if(i+1>=k) printf("%d ",a[q[hh]]);
if(i+1-q[hh]>=k) ++hh;
}
printf("\n");
tt = -1,hh = 0;
memset(q, 0, sizeof q);
for(int i = 0 ;i <n ;i ++){
while(tt>=hh && a[q[tt]]<a[i]){
--tt;
}
q[++tt] = i;
if(i+1>=k) printf("%d ",a[q[hh]]);
if(i+1-q[hh]>=k) ++hh;
}
return 0;
}
Acwing830. 单调栈
给定一个长度为
N
N
N 的整数数列,输出每个数左边第一个比它小的数,如果不存在则输出
−
1
−1
−1。
输入格式
第一行包含整数
N
N
N,表示数列长度。
第二行包含 N N N 个整数,表示整数数列。
输出格式
共一行,包含 N 个整数,其中第 i 个数表示第 i 个数的左边第一个比它小的数,如果不存在则输出 −1。
数据范围
1
≤
N
≤
1
0
5
1 \leq N \leq 10^5
1≤N≤105
1
≤
数
列
中
元
素
≤
1
0
9
1\leq数列中元素\leq10^9
1≤数列中元素≤109
输入样例:
5
3 4 2 7 5
输出样例:
-1 3 -1 2 2
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
int n;
int a[N];
int q[N];
int main(){
scanf("%d",&n);
int tt = 0;
for(int i = 1 ;i <= n ;i ++ ){ scanf("%d",&a[i]);}
for(int i = 1 ;i <= n; i ++ ){
while( tt && a[i] <= q[tt]){
q[--tt];
}
printf("%d ",tt == 0 ? -1 : q[tt]);
q[++tt] = a[i] ;
}
return 0;
}
2、并查集
3、树状数组:
- 用于单点修改,区间查询
- 用于区间修改,单点查询
- 用于区间修改,区间查询
用于单点修改,区间查询
Acwing241. 楼兰图腾
在完成了分配任务之后,西部 314 来到了楼兰古城的西部。
相传很久以前这片土地上(比楼兰古城还早)生活着两个部落,一个部落崇拜尖刀(V),一个部落崇拜铁锹(∧),他们分别用 V 和 ∧ 的形状来代表各自部落的图腾。
西部 314 314 314 在楼兰古城的下面发现了一幅巨大的壁画,壁画上被标记出了 n n n 个点,经测量发现这 n n n 个点的水平位置和竖直位置是两两不同的。
西部 314 314 314 认为这幅壁画所包含的信息与这 n 个点的相对位置有关,因此不妨设坐标分别为 ( 1 , y 1 ) , ( 2 , y 2 ) , … , ( n , y n ) (1,y_{1}),(2,y_{2}),…,(n,y_{n}) (1,y1),(2,y2),…,(n,yn),其中 y 1 ∼ y n y_{1}∼y_{n} y1∼yn 是 1 1 1 到 n n n 的一个排列。
西部 314 314 314 打算研究这幅壁画中包含着多少个图腾。
如果三个点 ( i , y i ) , ( j , y j ) , ( k , y k ) (i,y_i),(j,y_j),(k,y_k) (i,yi),(j,yj),(k,yk) 满足 1 ≤ i < j < k ≤ n 1\leq i<j<k\leq n 1≤i<j<k≤n 且 y i > y j , y j < y k y_i>y_j,y_j<y_k yi>yj,yj<yk,则称这三个点构成 V 图腾;
如果三个点 ( i , y i ) , ( j , y j ) , ( k , y k ) (i,y_i),(j,y_j),(k,y_k) (i,yi),(j,yj),(k,yk) 满足 1 ≤ i < j < k ≤ n 1\leq i<j<k\leq n 1≤i<j<k≤n 且 y i < y j , y j > y k y_i<y_j,y_j>y_k yi<yj,yj>yk,则称这三个点构成 ∧ 图腾;
西部 314 想知道,这 n 个点中两个部落图腾的数目。
因此,你需要编写一个程序来求出 V 的个数和 ∧ 的个数。
输入格式
第一行一个数
n
n
n。
第二行是 n n n 个数,分别代表 y 1 , y 2 , … , y n y_{1},y_{2},…,y_{n} y1,y2,…,yn。
输出格式
两个数,中间用空格隔开,依次为 V 的个数和 ∧ 的个数。
数据范围
对于所有数据,
n
≤
200000
n\leq200000
n≤200000,且输出答案不会超过 int
64
64
64。
y
1
∼
y
n
y_{1}∼y_{n}
y1∼yn 是
1
1
1 到
n
n
n的一个排列。
输入样例:
5
1 5 3 2 4
输出样例:
3 4
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+10;
int n;
int great[N],lower[N];
int q[N],tr[N];
typedef long long LL;
int lowbit(int x)
{
return x & -x;
}
void add(int x, int c) // 位置x加c
{
for (int i = x; i <= n; i += lowbit(i)) tr[i] += c;
}
int query(int x) // 返回前x个数的和
{
int res = 0;
for (int i = x; i; i -= lowbit(i)) res += tr[i];
return res;
}
int main(){
scanf("%d", &n);
for(int i = 1 ;i <= n ;i ++) scanf("%d", &q[i]);
for(int i = 1 ;i <= n ;i ++){
int y = q[i];
lower[i] = query(y-1);
great[i] = query(n) - query(y);
add(y,1);
}
memset(tr, 0, sizeof tr);
LL res1 =0, res2 = 0;
for(int i = n ; i; i--){
int y = q[i];
res1+= great[i] * (LL)(query(n)-query(y));
res2 += lower[i] * (LL)(query(y-1));
add(y,1);
}
printf("%lld %lld",res1,res2);
return 0;
}
Acwing244. 谜一样的牛
参考题解
有 n 头奶牛,已知它们的身高为 1∼n 且各不相同,但不知道每头奶牛的具体身高。
现在这 n n n 头奶牛站成一列,已知第 i i i头牛前面有 A i A_{i} Ai 头牛比它低,求每头奶牛的身高。
输入格式
第
1
1
1 行:输入整数
n
n
n。
第
2..
n
2..n
2..n 行:每行输入一个整数
A
i
A_{i}
Ai,第
i
i
i 行表示第
i
i
i 头牛前面有
A
i
A_{i}
Ai 头牛比它低。
(注意:因为第
1
1
1 头牛前面没有牛,所以并没有将它列出)
输出格式
输出包含
n
n
n 行,每行输出一个整数表示牛的身高。
第 i i i 行输出第 i i i 头牛的身高。
数据范围
1
≤
n
≤
1
0
5
1\leq n\leq10^{5}
1≤n≤105
输入样例:
5
1
2
1
0
输出样例:
2
4
5
3
1
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010;
int n;
int h[N];
int ans[N];
int tr[N];
int lowbit(int x)
{
return x & -x;
}
void add(int x, int c)
{
for (int i = x; i <= n; i += lowbit(i)) tr[i] += c;
}
int sum(int x)
{
int res = 0;
for (int i = x; i; i -= lowbit(i)) res += tr[i];
return res;
}
int main()
{
scanf("%d", &n);
for (int i = 2; i <= n; i ++ ) scanf("%d", &h[i]);
for (int i = 1; i <= n; i ++ ) tr[i] = lowbit(i);
for (int i = n; i; i -- )
{
int k = h[i] + 1;
int l = 1, r = n;
while (l < r)
{
int mid = l + r >> 1;
if (sum(mid) >= k) r = mid;
else l = mid + 1;
}
ans[i] = r;
add(r, -1);
}
for (int i = 1; i <= n; i ++ ) printf("%d\n", ans[i]);
return 0;
}
AcWing 243. 一个简单的整数问题2
给定一个长度为
N
N
N 的数列
A
A
A,以及
M
M
M条指令,每条指令可能是以下两种之一:
C l r d
,表示把 A [ l ] , A [ l + 1 ] , … , A [ r ] A[l],A[l+1],…,A[r] A[l],A[l+1],…,A[r] 都加上 d d d。Q l r
,表示询问数列中第 l ∼ r l∼r l∼r 个数的和。
对于每个询问,输出一个整数表示答案。
输入格式
第一行两个整数
N
,
M
N,M
N,M。
第二行 N N N个整数 A [ i ] A[i] A[i]。
接下来 M M M行表示 M M M条指令,每条指令的格式如题目描述所示。
输出格式
对于每个询问,输出一个整数表示答案。
每个答案占一行。
数据范围:
1
≤
N
,
M
≤
105
1 \leq N,M\leq105
1≤N,M≤105,
∣
d
∣
≤
10000
|d|\leq 10000
∣d∣≤10000,
∣
A
[
i
]
∣
≤
109
|A[i]|\leq 109
∣A[i]∣≤109
输入样例:
10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4
输出样例:
4
55
9
15
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 100010;
int n, m;
int a[N];
LL tr1[N]; // 维护b[i]的前缀和
LL tr2[N]; // 维护b[i] * i的前缀和
int lowbit(int x)
{
return x & -x;
}
void add(LL tr[], int x, LL c)
{
for (int i = x; i <= n; i += lowbit(i)) tr[i] += c;
}
LL sum(LL tr[], int x)
{
LL res = 0;
for (int i = x; i; i -= lowbit(i)) res += tr[i];
return res;
}
LL prefix_sum(int x)
{
return sum(tr1, x) * (x + 1) - sum(tr2, x);
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
for (int i = 1; i <= n; i ++ )
{
int b = a[i] - a[i - 1];
add(tr1, i, b);
add(tr2, i, (LL)b * i);
}
while (m -- )
{
char op[2];
int l, r, d;
scanf("%s%d%d", op, &l, &r);
if (*op == 'Q')
{
printf("%lld\n", prefix_sum(r) - prefix_sum(l - 1));
}
else
{
scanf("%d", &d);
// a[l] += d
add(tr1, l, d), add(tr2, l, l * d);
// a[r + 1] -= d
add(tr1, r + 1, -d), add(tr2, r + 1, (r + 1) * -d);
}
}
return 0;
}