題解:CF2025E Card Game

ccxswl發表於2024-11-13

在這

貌似和大部分做法不太一樣(?)


權當卡特蘭數的複習題吧。不會卡特蘭數的可以先看文末。


如果沒有花色 \(1\) 這道題就很簡單了,對於每個別的花色都有 \(C(m)\) 種分配方案。\(C(n)\) 表示卡特蘭數的第 \(n\) 項,答案就是乘起來。

發現除了花色 \(1\) 每種花色的牌都是獨立的。這啟示我們列舉每種牌用了多少張花色 \(1\)。設 \(f_{i,j}\) 表示前 \(i\) 種花色的牌用了 \(j\) 張花色 \(1\) 能使第一名玩家獲勝的方案數。

有轉移:

\[f_{i,j}=\sum_{k\le j} f_{i-1,j-k} \times H(k) \]

列舉的 \(k\) 的含義為第 \(i\) 個花色用了 \(k\) 張花色 \(1\)。其中 \(H(k)\) 表示用了 \(k\) 張花色 \(1\) 的分配方案。

\(H(k)\) 的計算也是簡單的,考慮卡特蘭數的經典問題,不能經過直線 \(y=x\) 的方格路計數(如果不會可以看文末)。我們先得到了 \(k\) 張極大的牌,可以看做在網格中先向右走了 \(k\) 步,那問題就成了從 \((0,k)\) 走到 \((\frac{m+k}{2},\frac{m+k}{2})\) 且不能經過直線 \(y=x\) 的方案數。

答案就是:

\[H(k)={m \choose \frac{m+k}{2}}-{m\choose\frac{m+k}{2}+1} \]

至此,已經解決大部分問題了,最後就是解決花色 \(1\) 的分配。上面的問題是 \(m\) 張牌中需要先拿出 \(k\) 張與花色 \(1\) 匹配,而現在的問題是 \(m\) 張花色 \(1\) 需要拿出若干張與其他牌匹配,發現問題是一樣的,統計答案時乘上 \(H(k)\) 就好了,這裡的 \(k\) 表示有 \(k\) 張花色 \(1\) 被用來與其他花色的牌匹配。

複雜度為 \(O(n^3)\),如果你是大常數選手就會 TLE,有一個最佳化是 \(\frac{m+k}{2}\) 為整數的狀態才合法,加上這個就不卡常了。

#include <bits/stdc++.h>

using namespace std;

const int maxN = 1e3 + 7, mod = 998244353;

int fc[maxN], ifc[maxN];
int ksm(int a, int b = mod - 2) {
  int res = 1;
  while (b) {
    if (b & 1)
      res = 1ll * res * a % mod;
    a = 1ll * a * a % mod;
    b >>= 1;
  }
  return res;
}

int n, m;

int C(int n, int m) {
  if (n < 0 || m < 0 || n < m) return 0;
  return 1ll * fc[n] * ifc[m] % mod * ifc[n - m] % mod;
}
int H(int k) {
  auto res = C(m, (m + k) / 2) - C(m, (m + k) / 2 + 1);
  res += res < 0 ? mod : 0;
  return res;
}

int f[507][507];

signed main() {
  ios::sync_with_stdio(false), cin.tie(nullptr);

  cin >> n >> m;

  fc[0] = 1;
  for (int i = 1; i <= m * 2; i++)
    fc[i] = 1ll * fc[i - 1] * i % mod;
  ifc[m * 2] = ksm(fc[m * 2]);
  for (int i = m * 2 - 1; i >= 0; i--)
    ifc[i] = 1ll * ifc[i + 1] * (i + 1) % mod;

  f[1][0] = 1;
  for (int i = 2; i <= n; i++)
    for (int j = 0; j <= m; j++)
      for (int k = (m & 1); k <= j; k += 2)
        f[i][j] = (f[i][j] + 1ll * f[i - 1][j - k] * H(k) % mod) % mod;

  int ans = 0;
  for (int k = 0; k <= m; k++)
    ans = (ans + 1ll * f[n][k] * H(k) % mod) % mod;
  cout << ans << '\n';
}

卡特蘭數可以解決這類問題:有一個大小為 \(n\times n\) 的方格圖左下角為 \((0, 0)\) 右上角為 \((n, n)\) ,從左下角開始每次都只能向右或者向上走一單位,不走到對角線 \(y=x\) 上方(但可以觸碰)的情況下到達右上角有多少可能的路徑?(摘自 OI Wiki)

答案就是所有的路徑數減去不合法的路徑數,發現不合法的路徑一定經過直線 \(y=x+1\)。對於經過 \(y=x+1\) 且終點為 \((n,n)\) 的路徑,在它第一次接觸 \(y=x+1\) 後,把它的所有運動反向,向右走變為向上走,向上走變為向右走,最後它一定會到達 \((n-1,n+1)\)

下圖中的橙色虛線和粉色虛線是不合法的路徑。

可以得出 \(C(n)\) 為從 \((0,0)\)\((n,n)\) 的路徑數減從 \((0,0)\)\((n-1,n+1)\) 的路徑數。

所以:

\[C(n)={2n\choose n}-{2n\choose n+1} \]

相關文章