[JSOI2008] 最大數 (單調棧)

Yorg發表於2024-10-02

演算法

基礎

發現插入總在最後一個進行

單調棧維護一個區間的 \(max / min\) 單調佇列維護以一個值為 \(max / min\) 的最大區間

顯然可以使用單調棧維護
其原理為

\(a, b \in seq, a < b, pos[a] < pos[b]\)
那麼顯然 \(a\) 沒有卵用

因此可以用單調棧維護一個包含 \(seq\) 的最後一個元素的單調遞減序列
求解區間 \([L, Last]\) 的值, 即為求解在 \([L, Last]\) 這一區間內第一個存在於單調棧中的元素

最佳化

二分 ( \(O(n\log n)\) )

顯然可以二分查詢
不加贅述

並查集 (\(O(n)\))

對於一個下標 \(i\) 將它和在它之後第一個在單調佇列裡的數的下標放在同一個並查集中
這樣可以保證在插入的過程中仍然可以 \(O(1)\) 處理查詢

#include <bits/stdc++.h>
const int MAXSIZE = 2e5 + 20;
#define int long long

int M, D;

class Union_Set
{
    private:

    public:
        int fa[MAXSIZE];

        int find(int Point)
        {
            return fa[Point] = (Point == fa[Point]) ? Point : find(fa[Point]);
        }

        void insert(int Fa, int Son)
        {
            fa[find(Son)] = find(Fa);
        }
} US;

struct node
{
    int Val;
    int sub;
};

class MonoStack
{
    private:

    public:
        node Val[MAXSIZE];
        int top;

        void init()
        {
            top = 0;
        }

        void insert(int x, int sub)
        {
            while (Val[top].Val < x && top)
            {
                US.insert(sub, Val[top].sub);
                top--;    
            }
            Val[++top].sub = sub;
            Val[top].Val = x;
        }

} MS;

int t = 0;
int size = 0;
int Num[MAXSIZE];

void solve()
{
    MS.init();
    while(M--)
    {
        char type;
        std::cin >> type;
        if(type == 'A')
        {
            scanf("%lld", &Num[++size]);
            Num[size] = (Num[size] + t) % D;
            US.fa[size] = size; 
            MS.insert(Num[size], size);
        }else{
            int L;
            scanf("%lld", &L);
            L = size - L + 1;
            t = Num[US.find(L)];
            printf("%lld\n", t);
        }
    }
}

signed main()
{
    scanf("%lld %lld", &M, &D);

    solve();

    return 0;
}

總結

程式碼

注意在單調棧彈出是確保位置有意義
並查集插入時需要初始化

思路

並查集往往可以用來處理連續的鏈向問題

相關文章