题解: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;
}
posted @ 2026-08-22 21:41  Antony_pico  阅读(6)  评论(0)    收藏  举报