[AGC056B] Range Argmax

lalaouye發表於2024-10-04

發現一個序列 \(x\) 不止可以用一個 \(p\) 得到,肯定不能直接計數,考慮構造一個對映。

假如已經定下了 \(x\),我們透過一種固定的操作得到 \(p\),這樣就能改為統計可以由操作得到的 \(p\) 的數量,他們同樣唯一對應一個 \(x\)

我們考慮列舉從 \(n\)\(1\) 去列舉 \(v\),對每個 \(v\) 找到一個能找到的最左邊的點賦值。一個位置 \(pos\) 能賦值 \(v\) 當且僅當所有 \(l_i,r_i\) 包含 \(pos\)\(x_i\) 等於 \(pos\)

接下來我們就可以處理左區間和右區間的位置了。容易發現其右邊的值都要小於左邊的值,這將變成兩個子問題。

現在 \(x\) 並不固定,我們需要分析條件才能計數。

注意到,若當前我們在 \(pos\) 這裡賦值,那麼對於左側的最大值的位置 \(k\),一定存在一個區間滿足 \(l_i\le k\le pos\le r_i\),因為若沒有區間同時包含 \(k,pos\),那麼顯然我可以讓 \(p_{pos}=v\),所以一定存在這樣的區間。之所以存在這種情況那隻能是因為這個區間的 \(x_i\) 等於 \(pos\)。我們需要用一個大數在這裡填,填完以後再將區間刪去才能更新 \(k\)

這啟發我們進行一個區間 dp,設 \(f_{l,r,i}\) 表示在區間 \(l,r\) 中最大值位置在 \(i\) 的方案數。但是由於這樣子複雜度很劣,我們將狀態改為在區間 \(l,r\) 中最大值位置大於等於 \(i\) 的方案數就能 \(\mathcal{O}(n^3)\) 解決問題了,具體就是先維護區間如果要選 \(pos\) 賦值,包含 \(pos\) 的區間的最小 \(l_i\),然後再字尾和轉移即可。

程式碼:

#include <bits/stdc++.h>
#define int long long
#define rep(i, l, r) for (int i = l; i <= r; ++ i)
#define rrp(i, l, r) for (int i = r; i >= l; -- i)
#define pii pair <int, int>
#define eb emplace_back
#define id(x, y) n * (x - 1) + y
#define ls p << 1
#define rs ls | 1
using namespace std;
constexpr int N = 300 + 5, M = (1ll << 31) - 1, P = 998244353;
constexpr double PI = acos (-1.0);
inline int rd () {
  int x = 0, f = 1;
  char ch = getchar ();
  while (! isdigit (ch)) {
    if (ch == '-') f = -1;
    ch = getchar ();
  }
  while (isdigit (ch)) {
    x = (x << 1) + (x << 3) + ch - 48;
    ch = getchar ();
  }
  return x * f;
}
int qpow (int x, int y) {
  int ret = 1;
  for (; y; y >>= 1, x = x * x % P) if (y & 1) ret = ret * x % P;
  return ret;
}
int f[N][N][N], g[N][N][N];
int n, m;
long long fac[N], ifac[N];
int C (int n, int m) {
  if (m < 0 || m > n) return 0;
  return fac[n] * ifac[m] % P * ifac[n - m] % P;
}
int l[N * N], r[N * N];
signed main () {
  // freopen ("1.in", "r", stdin);
  // freopen ("1.out", "w", stdout);
  n = rd (), m = rd ();
  rep (i, 1, m) l[i] = rd (), r[i] = rd ();
  rep (i, 0, n + 1) rep (j, 0, n + 1) rep (k, 0, n + 1) g[i][j][k] = n + 1;
  rep (i, 1, m) {
    rep (j, l[i], r[i]) {
      g[l[i]][r[i]][j] = min (g[l[i]][r[i]][j], l[i]); 
    }
  }
  rep (len, 1, n) {
    rep (l, 1, n - len + 1) {
      int r = l + len - 1;
      rep (k, 1, n) g[l][r][k] = min (g[l][r][k], min (g[l + 1][r][k], g[l][r - 1][k]));
    }
  }
  rep (l, 1, n + 1) rep (k, 1, n + 1) f[l][l - 1][k] = 1;
  rep (len, 1, n) {
    rep (l, 1, n - len + 1) {
      int r = l + len - 1;
      rrp (k, l, r) {
        (f[l][r][k] += f[l][r][k + 1] + f[l][k - 1][g[l][r][k]] * f[k + 1][r][k + 1]) %= P;
      }
    }
  } cout << f[1][n][1];
}

相關文章