隐藏
「清北学堂3」排列 - 单调栈+双指针 | Bill Yang's Blog

路终会有尽头,但视野总能看到更远的地方。

0%

「清北学堂3」排列 - 单调栈+双指针

题目大意

给出一个随机的排列,请你计算最大值减最小值的差小于等于0~$n$-1的区间分别有多少个。


题目分析

此题可做完全基于随机性。
这题似乎有分治的解法。
然而我用的单调栈+双指针。

我们先预处理出$i$后比$i$大/小的第一个数的位置,可以使用单调栈维护。

然后我们枚举区间起点,维护一个最大值,一个最小值,找到最大值、最小值最早变化的地方,那么在变化的地方前,最大值最小值之差一定,我们可以$O(1)$更新。

这个做法最坏$O(n^2)$,但因为数据随机,所以可以过。


代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
#include<stack>
using namespace std;
typedef long long LL;
inline const LL Get_Int() {
LL num=0,bj=1;
char x=getchar();
while(x<'0'||x>'9') {
if(x=='-')bj=-1;
x=getchar();
}
while(x>='0'&&x<='9') {
num=num*10+x-'0';
x=getchar();
}
return num*bj;
}
const int maxn=100005;
LL n,a[maxn],NextMax[maxn],NextMin[maxn],Ans[maxn],pos[maxn];
int main() {
n=Get_Int();
for(int i=1; i<=n; i++)a[i]=Get_Int(),pos[a[i]]=i,NextMax[i]=NextMin[i]=n+1;
stack<int>S;
for(int i=1; i<=n; i++) {
while(!S.empty()&&a[S.top()]>a[i]) {
NextMin[S.top()]=i;
S.pop();
}
S.push(i);
}
S=stack<int>();
for(int i=1; i<=n; i++) {
while(!S.empty()&&a[S.top()]<a[i]) {
NextMax[S.top()]=i;
S.pop();
}
S.push(i);
}
for(int i=1; i<=n; i++) {
int Max=a[i],Min=a[i],j=i;
while(j<=n) {
if(NextMin[pos[Max]]<NextMax[pos[Min]]) {
Ans[Max-Min]+=NextMin[pos[Max]]-j;
j=NextMin[pos[Max]];
Max=a[NextMin[pos[Max]]];
} else {
Ans[Max-Min]+=NextMax[pos[Min]]-j;
j=NextMax[pos[Min]];
Min=a[NextMax[pos[Min]]];
}
}
}
for(int i=1; i<=n; i++)Ans[i]+=Ans[i-1],printf("%lld\n",Ans[i-1]);
return 0;
}
姥爷们赏瓶冰阔落吧~