Solution - Atcoder ARC127E Priority Queue

rizynvu發表於2024-07-12

考慮轉化一下,每個最後留下來的集合都相對的對應著一個被刪除的集合。
於是考慮去對被刪除的數去計數。

然後貪心的,去讓每一次 \(2\) 操作刪除的數都是前面加入中還剩下的最後加入的數(因為有的可能被前面的 \(2\) 操作刪了)。
對於證明,考慮到如果不是剩下的最後加入的,那麼中間可能會有 \(2\) 操作截胡了,且把其調整到最後加入肯定不劣。

於是可以先把這些已經確定要被刪去的 \(1\) 操作刪掉,被這個操作一起考慮到 \(2\) 操作上。
那麼此時 \(2\) 操作的定義就是把一個大於當前已有的數的集合的沒選過的數加入到刪除的集合裡。

於是可以知道對於一個 \(2\) 操作,其選的數一定會大於前面每個 \(1\) 操作選的數。
但是此時 \(2\) 操作之間的大小關係還沒確定,還是不是很好做。

但是,憑直覺去想一想,可以知道同樣可以貪心的,讓後面的 \(2\) 選的數大於前面的 \(2\) 選的數。
證明考慮記第 \(i\)\(2\) 操作選到的數為 \(s_i\),那麼如果有 \(i < j, s_i > s_j\),那麼肯定 \(j\) 之前選的 \(1\) 都沒有 \(s_j\) 大,因為 \(i < j\) 於是讓 \(i\) 位置選 \(s_j\) 肯定滿足,因為又有 \(s_i > s_j\) 所以 \(j\) 位置選 \(s_i\) 肯定也滿足。

於是可以知道 \(2\) 操作選的數需要大於所有前面操作選的數。
記第 \(i\)\(2\) 操作在操作序列中是第 \(l_i\) 個,所以可以知道這次操作選的數的區間就是 \([l_i, a]\)

同時又有 \(2\) 操作選的數是遞增的,於是考慮 DP
\(f_{i, j}\) 為第 \(i\)\(2\) 操作選的數為 \(j\) 的方案數,那麼有轉移 \(f_{i, j} = \begin{cases} 0 & j < l_i\\ \sum\limits_{k = 0}^{j - 1} f_{i - 1, k} & j \ge l_i\end{cases}\)
然後用個字首和最佳化就可以了。

時間複雜度 \(\mathcal{O}(ab)\)

#include<bits/stdc++.h>
constexpr int mod = 998244353;
const int maxn = 1e4 + 10;
int x[maxn], l[maxn];
int f[maxn];
int main() {
   int a, b;
   scanf("%d%d", &a, &b);
   for (int i = 1; i <= a + b; i++)
      scanf("%d", &x[i]);
   for (int i = a + b, tot = 0; i; i--) {
      if (x[i] == 2) tot++;
      else if (tot) x[i] = 0, tot--;
   }
   for (int i = 1, tot = 0, n = 0; i <= a + b; i++) {
      tot += x[i] > 0;
      if (x[i] == 2) l[++n] = tot;
   }
   for (int i = 0; i <= a; i++)
      f[i] = 1;
   for (int i = 1; i <= b; i++) {
      for (int j = a; j >= l[i]; j--)
         f[j] = f[j - 1];
      for (int j = l[i] - 1; ~ j; j--)
         f[j] = 0;
      for (int j = 1; j <= a; j++)
         (f[j] += f[j - 1]) %= mod;
   }
   printf("%d\n", f[a]);
   return 0;
}

相關文章