1. 程式人生 > 實用技巧 >CodeForces-631C Report 單調棧,思維

CodeForces-631C Report 單調棧,思維

CodeForces-631C Report 單調棧,思維

題意

給定一個初始序列\(a\)

輸出經過\(m\)次操作後的序列

每個操作是兩種之一

  • \(1 \quad r\) ,將序列\([1,r]\) 從小到大排序
  • \(2\quad r\) ,將序列\([1,r]\)從大到小排序

分析

暴力顯然是不可取的。

注意到性質

對於操作\(i,j\),若$i > j \(且\)r_i > r_j$ 那麼這個\(j\)是無效的。

這提示我們利用這個性質縮小範圍。

根據\(r\)維護一個單調遞減的單調棧。每次我們只需要考慮兩個相鄰的棧的\(r\)之間的元素。

並且由於之前最大的\(r\)

已經把\([1,r]\)排好序了。接下來每次都只是用雙指標選出排好序中連續的一段。

這樣就可以\(O(nlogn)\)實現,瓶頸在於排序。

程式碼

int a[200005];
int b[200005];
int c[200005];
int op[200005];
int st[200005];

int main() {
    int n = readint();
    int m = readint();
    int ptr = 0;
    for (int i = 0; i < n; i++)
        a[i] = readint();
    for (int i = 0; i < m; i++) {
        int tmp = readint();
        c[i] = readint();
        while (ptr && c[st[ptr - 1]] < c[i])
            ptr--;
        st[ptr] = i, op[ptr++] = tmp;
    }
    int pos = c[st[0]];
    for (int i = 0; i < pos; i++)
        b[i] = a[i];
    sort(b, b + pos);
    int l = 0;
    int r = c[st[0]];
    for (int i = 1; i < ptr; i++) {
        for (int j = c[st[i - 1]]; j > c[st[i]]; j--)
            a[j - 1] = (op[i - 1] == 1) ? b[--r] : b[l++];
    }
    if (ptr - 1 >= 0) {
        for (int i = c[st[ptr - 1]]; i > 0; i--)
            a[i - 1] = (op[ptr - 1] == 1) ? b[--r] : b[l++];
    }
    for (int i = 0; i < n; i++)
        printf("%d ", a[i]);
}