P8475 「GLR-R3」雨水 題解

Night_Tide發表於2024-09-26

關於這道題目卡 \(O(n\log n)\) 但是放 \(O(n^2)\) 我也是很疑惑。

我們發現,題目要求的是字典序最小的序列。但凡涉及了字典序最小,答案或多或少的都會帶點貪心思想。

那我們也來貪一貪。考慮當前列舉到第 \(i\) 個點,如果後面有比它更小的數,那顯然把它們交換過來是更優的。如果有多個,那顯然也是最小的那一個交換過來是最優的。如果最小的還有多個,那肯定把當前這個和最後一次出現的交換是最優的。

同時,由於題目要求 \(P\)\(\lang 1,2,\dots,n\rang\) 的一個子序列,所以假設我們這次將 \(i\)\(j\) 位置上的元素交換了,接下來就不能對下標 \(k\le j\) 的元素動手動腳了,下一個列舉到的點就是 \(j + 1\) 了。

如果這麼說還不明白的話,我們模擬一下樣例:


樣例一:

1 1 3 0 0 1 3
↑       ↑
i       j

起初,i = 1,我們發現 i 以後最小的數是 0,它是小於 a[i] 的,而 j = 5 是 0 最後一次出現的位置,所以將 a[1] 和 a[5] 交換。

0 1 3 0 1 1 3
          ↑
          i

接下來 i 移動到 6 的位置,發現後面沒有小於 a[i] 的數了。

0 1 3 0 1 1 3
            ↑
            i

最後,i 移動到 7,程式結束。

樣例二:

2 8 0 6 2 2 1 7 8 3
↑   ↑
i   j

起初,i = 1,我們發現 i 以後最小的數是 0,它是小於 a[i] 的,而 j = 3 是 0 最後一次出現的位置,所以將 a[1] 和 a[3] 交換。

0 8 2 6 2 2 1 7 8 3
      ↑     ↑
      i     j

接下來 i 移動到 4 的位置,我們發現 i 以後最小的數是 1,它也是小於 a[i] 的,而 j = 7 是 0 最後一次出現的位置,所以將 a[4] 和 a[7] 交換。

0 8 2 1 2 2 6 7 8 3
              ↑   ↑
              i   j

接下來 i 移動到 8 的位置,我們發現 i 以後最小的數是 3,它同樣是小於 a[i] 的,而 j = 10 是 3 最後一次出現的位置,所以將 a[8] 和 a[10] 交換。

0 8 2 1 2 2 6 3 8 7
                    ↑
                    i

最後,i 移動到 11,程式結束。

至於實現,我們可以維護一個字尾最小值最後一次出現的位置 \(p\),而且是最後一次出現的位置,然後將 \(a_i\)\(a_{p_i}\) 交換,再令 \(i = p_i + 1\),然後重複以上步驟直到 \(i \ge n\)

我的程式中是用 \(nxt\) 來儲存字尾最小值最後一次出現的位置的,大家看的時候轉換一下就好:

#include<bits/stdc++.h>
#define MAXN 10000010
using namespace std;
typedef unsigned long long ull;
int n, m, k;
int a[MAXN], nxt[MAXN];
namespace Generator{
    ull k1, k2;
    int thres;
    inline ull xorShift128Plus() {
        ull k3 = k1, k4 = k2;
        k1 = k4, k3 ^= (k3 << 23), k2 = k3 ^ k4 ^ (k3 >> 17) ^ (k4 >> 26);
        return k2 + k4;
    }
    inline void generate() {
        for (int i = 1; i <= n; ++i) {
            a[i] = xorShift128Plus() % thres;
        }
    }
}
int main(){
    scanf("%d%llu%llu%d", &n, &Generator::k1, &Generator::k2, &Generator::thres);
    Generator::generate();
    nxt[n] = n;
    for(int i = n - 1; i >= 1; i--){
        if(a[i] < a[nxt[i + 1]]) nxt[i] = i;
        else nxt[i] = nxt[i + 1];
    }
    for(int i = 1; i < n; i++){
        if(a[nxt[i + 1]] < a[i]){
            swap(a[i], a[nxt[i + 1]]);
            i = nxt[i + 1];
        }
    }
    ull ans = 0;
    for(int i = 1; i <= n; i++) ans += (ull)i * a[i];
    printf("%llu\n",ans);
    return 0;
}