洛谷 P13973 [VKOSHP 2024] Nightmare Sum

先用单调栈预处理出 i 位置左右第一个小于 a[i] 的位置,然后计算出 tot 数组 (tot[i]: 所有以 a[i] 为最小值的子数组总数) 和 pos 数组去记录每个数的位置所在 (每个数互不相同)。构造离线查询,对于固定的 i,枚举所有正整数 t 使得 t * a[i] <= maxA,把这个 t 对应的阈值 T = t * a[i] - 1 上记一条查询 “询问以 a[i] 为最小值且最大值 ≤ T 的子数组个数”。queries[T] 存的就是所有需要在阈值 T 下回答的索引 i,最后按 T 倒序维护值 > T 的位置集合并回答查询,然后容斥一下 tot[i] * t 减去这些子数组最大值不是 maxA 的数量就是最后答案。

#include <bits/stdc++.h>

// https://www.luogu.com.cn/problem/P13973
// from: cpp.json
#define INF 8e18
#define LD long double
#define i128 __int128_t
#define int long long
using namespace std;

void solve() {
    int n;
    cin >> n;

    vector<int> a(n + 1);
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }

    int maxA = *max_element(a.begin(), a.end());
    vector<int> L(n + 1), R(n + 1);
    {
        vector<int> stk;
        for (int i = 1; i <= n; i++) {
            while (!stk.empty() && a[stk.back()] > a[i]) {
                stk.pop_back();
            }
            L[i] = stk.empty() ? 0 : stk.back();
            stk.push_back(i);
        }
    }
    {
        vector<int> stk;
        for (int i = n; i >= 1; i--) {
            while (!stk.empty() && a[stk.back()] > a[i]) {
                stk.pop_back();
            } 
            R[i] = stk.empty() ? n + 1 : stk.back();
            stk.push_back(i);
        }
    }

    // 所有以 a[i] 为最小值的子数组总数
    vector<int> tot(n + 1);
    for (int i = 1; i <= n; i++) {
        tot[i] = (i - L[i]) * (R[i] - i);
    }

    vector<int> pos(maxA + 1);
    for (int i = 1; i <= n; i++) {
        pos[a[i]] = i;
    }

    vector<vector<int>> queries(maxA);
    for (int i = 1; i <= n; i++) {
        int v = a[i];
        int limit = maxA / v;
        for (int t = 1; t <= limit; t++) {
            int T = t * v - 1;
            queries[T].push_back(i);
        }
    }

    set<int> st;
    int sum = 0;
    for (int T = maxA - 1; T >= 0; T--) {
        if (pos[T + 1] != 0) {
            st.insert(pos[T + 1]);
        }
        for (auto idx : queries[T]) {
            auto it = st.lower_bound(idx);
            int r = (it == st.end() ? n + 1 : *it);
            int l;
            if (it == st.begin()) {
                l = 0;
            } else {
                auto it2 = it;
                it2--;
                l = *it2;
            }
            int left = max(L[idx], l);
            int right = min(R[idx], r);
            if (left < idx && right > idx) {
                int cnt = (idx - left) * (right - idx);
                sum += cnt;
            }
        }
    }

    int ans = 0;
    for (int i = 1; i <= n; i++) {
        int t = maxA / a[i];
        ans += tot[i] * t;
    }
    ans -= sum;

    cout << ans << '\n';
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    solve();

    return 0;
}
posted @ 2025-09-22 23:55  カガリ  阅读(32)  评论(0)    收藏  举报