题解:CF5E Bindian Signalizing
题目链接:https://codeforces.com/problemset/problem/5/E
前置知识:单调栈
题目大意:给定一个环形序列 \(a_1,a_2,\dots a_n\),\(a_1,a_n\) 首尾相连。求点对 \((i,j)\) 满足 \(\max\{a_{i+1},a_{i+2},\dots a_{j-1}\}\leq\min\{a_i,a_j\}\) 或 \(\max\{a_{j+1},\dots,a_n,a_1,\dots,a_{i-1}\}\leq\min\{a_i,a_j\}\) 的数量(\(i<j\))。\(n\leq 10^6\)。
弱化序列
弱化版(不是环形):https://www.luogu.com.cn/problem/P1823
考虑序列如何处理,不难想到单调栈,维护一个单调递减的栈。当遍历到一个新的点时,不断弹出栈顶 \(s_{top}\leq a_i\) 并累加答案,这些被弹掉的元素不可能在与后面的元素组成贡献,因为会被 \(i\) 挡住,然后如果栈非空,那么 ans++(栈顶可以和 \(i\) 组成贡献)。
因为会有相同的 \(a_i\),所以用 pair 维护,第二维放个数即可。弱化版代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6+5;
stack<pair<int, int>> st;
int n, ans, a[N];
signed main(){
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n;
for(int i = 1; i <= n; i++){
cin >> a[i];
int cnt = 1;
while(!st.empty() && st.top().first <= a[i]){
ans += st.top().second;
if(st.top().first == a[i]) cnt += st.top().second;
st.pop();
}
if(!st.empty()) ans++;
st.push({a[i], cnt});
}
cout << ans << '\n';
return 0;
}
环形处理
注意到环形并不好处理,于是断环为链。最高峰一定不能隔在两个可以答案点对中间,所以将最高峰作为序列的头。
但是这会漏掉情况,如 5 4 2 3 1,第一和第四、第五就会被漏掉,单独统计第一与其它点的贡献即可。时间复杂度 \(O(n)\)。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6+5;
stack<pair<int, int>> st;
int n, ans, maxx, start = 1, a[N];
bool hd[N];
signed main(){
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n;
for(int i = 1; i <= n; i++){
cin >> a[i];
if(a[i] > maxx) maxx = a[i], start = i;
a[i + n] = a[i];
}
for(int i = start; i <= start + n - 1; i++){
int cnt = 1;
if(a[i] == a[start]) hd[i] = true;
while(!st.empty() && a[st.top().first] <= a[i]){
ans += st.top().second;
if(a[st.top().first] == a[i]) cnt += st.top().second;
st.pop();
}
if(!st.empty()) ans++, hd[i] = st.top().first == start;
st.push({i, cnt});
}
int now_max = 0;
for(int i = start + n - 1; i > start; i--){
now_max = max(now_max, a[i]);
if(!hd[i] && now_max <= a[i]) ans++;
}
cout << ans << '\n';
return 0;
}

浙公网安备 33010602011771号