1. 程式人生 > 其它 >P7961 [NOIP2021] 數列 題解

P7961 [NOIP2021] 數列 題解

我好菜啊,動態規劃一點不會,一年前的題目,還要想接近 \(2h\) 才會。

考慮 \(a\) 陣列順序並沒有影響,於是全部由順序放置。

然後,最後的方案數通過組合數計算。

因為值域限制很大,於是對進位制進行 dp。

設立 \(dp_{i,j,k,p}\) 表示當前討論到 \(S\) 中第 \(i\) 位,\(a\) 中已經有了 \(j\) 個元素被確定,當前有了 \(k\)\(1\) 存在,\(i - 1\) 位向當前 \(i\) 位進位 \(p\)\(1\)

考慮刷表法,列舉選了 \(t\)\(i\),於是 \(dp_{i,j,k,p} \rightarrow dp_{i+1,j+t,k + (t + p) \bmod 2,\frac{p + t}{2}}\)

然後轉移的貢獻為 \(dp_{i,j,k,p} \times v_i^t \times \binom{n-j}{t}\)

然後統計答案隨便弄弄就完了,真的菜,當時這都不會。

// 德麗莎你好可愛德麗莎你好可愛德麗莎你好可愛德麗莎你好可愛德麗莎你好可愛
// 德麗莎的可愛在於德麗莎很可愛,德麗莎為什麼很可愛呢,這是因為德麗莎很可愛!
#include <bits/stdc++.h>
#define int long long
using namespace std;
inline int read() {
  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;
}
const int mod = 998244353, N = 700;
int n, m, ans, kqwq, v[N], fac[N], ifac[N], dp[N][65][65][65], c[N][N];
int power(int a,int b) { int ans = 1; while (b) { if (b & 1) ans = ans * a % mod; b >>= 1; a = a * a % mod; } return ans; }
int binom(int n,int m) { return c[n][m]; }
signed main () {
  n = read(), m = read(), kqwq = read();
  for (int i = 0; i <= m; i++) v[i] = read(); 
  dp[0][0][0][0] = 1; c[0][0] = 1;
  for (int i = 0; i <= n; i++) {
    c[i][0] = 1; c[i][i] = 1;
    for (int j = 1; j < i; j++) 
      c[i][j] = c[i - 1][j]  + c[i - 1][j - 1], c[i][j] %= mod;
  }
  for (int i = 0; i <= m; i++) {
    for (int j = 0; j <= n; j++) {
      for (int k = 0; k <= kqwq; k++) {
        for (int p = 0; p <= (n / 2); p++) {
          for (int t = 0; t <= n; t++) {
            if (t + j > n) continue;
            dp[i + 1][j + t][k + (p + t) % 2][(p + t) / 2] += dp[i][j][k][p] * binom(n - j, t) % mod * power(v[i], t) % mod;
            dp[i + 1][j + t][k + (p + t) % 2][(p + t) / 2] %= mod; 
            //cout << i << " " << j << " " << k << " " << p << " " << power(v[i], t) <<" " << binom(n - j, t)<< " qwq\n";
            //cout << i + 1 << " " << j + t << " " << (k + (j + t) % 2) << " " << (j + t) / 2 << " " << dp[i + 1][j + t][k +]
          }
        }
      }
    }
  }
  int ans = 0;
  for (int k = 0; k <= kqwq; k++) {
    for (int p = 0; p <= (n / 2); p++) {
      if (k + __builtin_popcount(p) <= kqwq) ans += dp[m + 1][n][k][p], ans %= mod;
    }
  }
  printf("%lld\n", ans);
  return 0;
}