CF1603E A Perfect Problem 題解

下蛋爷發表於2024-09-13

Description

稱一個序列為好序列當且僅當這個序列的 \(\max\times \min\ge sum\),其中 \(sum\) 是序列元素和。

給定 \(n,M\),求長度為 \(n\),每個數在 \([1,n+1]\) 範圍內,每個非空子序列(包含序列本身)都是好序列的整數序列個數,對 \(M\) 取模。

\(1\le n\le 200\)\(10^8\le M\le 10^9\),保證 \(M\) 為素數。

Solution

容易發現可以將序列排序後轉化為判斷所有字首是否合法。這樣就可以暴力 dp 了,時間複雜度:\(O(n^6)\)

考慮最佳化。下面有合法序列的幾條性質:

  1. \(\forall k,a_k\geq k\),因為如果 \(a_k<k\),則 \(a_1a_k<k\cdot a_1\leq \sum_{i=1}^{k}{a_i}\),矛盾了。

  2. \(a_k=k\),則 \(a_1=a_2=\ldots=a_k=k\)。因為 \(a_1a_k=k\cdot a_1\geq\sum_{i=1}^{k}{a_i}\),所以 \(a_1=a_2=\ldots=a_k=k\)

  3. \(a_n=n+1\),則 \(\forall a_k\geq k+1\)\([1,k]\) 合法。因為 \(a_1a_n=(n+1)a_1\geq\sum_{i=1}^{n}{a_i}\),則 \(a_1\geq\sum_{i=1}^{n}{(a_i-a_1)}\geq\sum_{i=1}^{k}{(a_i-a_1)}\),所以 \(a_1a_k\geq (k+1)a_1\geq\sum_{i=1}^{k}{a_i}\)


對於 \(a_n=n\) 的情況很容易。

對於 \(a_n=n+1\),一個序列合法的條件即為:

  • \(\forall 1\leq i\leq a_1,a_1\leq a_i\leq n+1\)

  • \(\forall a_1+1\leq i\leq n,i+1\leq a_i\leq n+1\)

  • \(\sum_{i=1}^{n}{(a_i-a_1)}\geq a_1\)

\(b_i=a_i-a_1\),則:

  • \(\forall 1\leq i\leq a_1,0\leq b_i\leq n+1-a_1\)

  • \(\forall a_1+1\leq i\leq n,i+1-a_1\leq b_i\leq n+1-a_1\)

  • \(\sum_{i=1}^{n}{b_i}\geq a_1\)

這樣就可以 dp 了。先列舉 \(a_1\) 的值,可以設 \(f_{i,j,k}\) 填了 \(b\) 的前 \(i\) 位,和為 \(j\),當前的最大值為 \(k\) 的方案數。

轉移時可以列舉 \(0\sim k-1\) 的總個數 \(i\)\(b\) 的前 \(i\) 項的和 \(j\)\(k\) 的出現次數 \(cnt\),則當 \(i+cnt+1-a_1\leq k\leq n+1-a_1\) 時,即可讓 \(f_{i+cnt,j+cnt\cdot k,k}\leftarrow \frac{f_{i,j,k-1}}{cnt!}\)

時間複雜度:\(O(n^4\log n)\),過不了。


注意到 \(a_1\) 的值不會很大,並且 \(a_1\geq n-2\sqrt n\)。證明就考慮 \(a_1\geq\sum b_i\geq\sum_{i=a_1+1}^{n}{(i-a_1+1)}=\frac{(n+a_1+3)(n-a_1)}{2}-a_1(n-a_1)\),可以得到 \(a_1\geq n-2\sqrt n\)

這樣 \(a_1\) 的列舉數量就只有 \(O(\sqrt n)\) 級別了。

時間複雜度:\(O(n^3\sqrt n\log n)\)

Code

#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 205;

int n, mod;
int fac[kMaxN], ifac[kMaxN], f[kMaxN][kMaxN][kMaxN];

constexpr int qpow(int bs, int64_t idx = mod - 2) {
  int ret = 1;
  for (; idx; idx >>= 1, bs = (int64_t)bs * bs % mod)
    if (idx & 1)
      ret = (int64_t)ret * bs % mod;
  return ret;
}

inline int add(int x, int y) { return (x + y >= mod ? x + y - mod : x + y); }
inline int sub(int x, int y) { return (x >= y ? x - y : x - y + mod); }
inline void inc(int &x, int y) { (x += y) >= mod ? x -= mod : x; }
inline void dec(int &x, int y) { (x -= y) < 0 ? x += mod : x; }

void prework() {
  fac[0] = ifac[0] = fac[1] = ifac[1] = 1;
  for (int i = 2; i <= n + 1; ++i) {
    fac[i] = 1ll * i * fac[i - 1] % mod;
    ifac[i] = qpow(fac[i]);
  }
}

void dickdreamer() {
  std::cin >> n >> mod;
  prework();
  int ans = 1;
  for (int a1 = std::max<int>(n - 18, 1); a1 <= n; ++a1) {
    for (int i = 0; i <= n; ++i)
      for (int j = 0; j <= a1; ++j)
        for (int k = 0; k <= n - a1 + 1; ++k)
          f[i][j][k] = 0;
    for (int i = 1; i <= a1; ++i) {
      f[i][0][0] = 1ll * fac[n] * ifac[i] % mod;
    }
    for (int k = 1; k <= n - a1 + 1; ++k) {
      for (int i = 1; i <= n; ++i) {
        for (int j = 0; j <= a1; ++j) {
          for (int cnt = 0; cnt <= std::min(n - i, (a1 - j) / k); ++cnt) {
            if (k >= i + cnt - a1 + 1) {
              inc(f[i + cnt][j + cnt * k][k], 1ll * f[i][j][k - 1] * ifac[cnt] % mod);
            }
          }
        }
      }
    }
    for (int i = 0; i <= a1; ++i) inc(ans, f[n][i][n - a1 + 1]);
  }
  std::cout << ans << '\n';
}

int32_t main() {
#ifdef ORZXKR
  freopen("in.txt", "r", stdin);
  freopen("out.txt", "w", stdout);
#endif
  std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
  int T = 1;
  // std::cin >> T;
  while (T--) dickdreamer();
  // std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
  return 0;
}

相關文章