結果
問題 |
No.1696 Nonnil
|
ユーザー |
|
提出日時 | 2025-08-05 21:06:26 |
言語 | C++23 (gcc 13.3.0 + boost 1.87.0) |
結果 |
WA
|
実行時間 | - |
コード長 | 3,819 bytes |
コンパイル時間 | 3,418 ms |
コンパイル使用メモリ | 282,672 KB |
実行使用メモリ | 31,312 KB |
最終ジャッジ日時 | 2025-08-05 21:07:14 |
合計ジャッジ時間 | 37,410 ms |
ジャッジサーバーID (参考情報) |
judge4 / judge3 |
(要ログイン)
ファイルパターン | 結果 |
---|---|
sample | AC * 3 WA * 1 |
other | AC * 31 WA * 8 |
ソースコード
#include <bits/stdc++.h> using namespace std; using i64 = long long; const int MOD = 998244353; // 快速幂 i64 mod_pow(i64 a, i64 e){ i64 r = 1; while(e){ if(e & 1) r = r * a % MOD; a = a * a % MOD; e >>= 1; } return r; } int add(int a, int b){ a += b; if(a >= MOD) a -= MOD; return a; } int sub(int a, int b){ a -= b; if(a < 0 ) a += MOD; return a; } int mul(i64 a, i64 b){ return int(a * b % MOD); } int main(){ ios::sync_with_stdio(false); cin.tie(nullptr); i64 N; int K; cin >> N >> K; int M; cin >> M; // forbiddenLen[r] = 在位置 r 结束的所有区间中,最短的那个区间长度 // 如果没有任何区间以 r 结束,设为 K+1(即不受限制) vector<int> forbiddenLen(K+1, K+1); for(int i = 0; i < M; i++){ int L, R; cin >> L >> R; int len = R - L + 1; forbiddenLen[R] = min(forbiddenLen[R], len); } // dp_prev[t][l]: 在处理到 pos-1 时,缺失集合中已经选了 t 个点, // 且末尾连续“1”(缺失)的长度为 l 的方案数。 // 我们用两个 2D 数组交替滚动。 vector<vector<int>> dp_prev(K+1), dp_cur(K+1); // 初始化 pos=0:还没选任何点,t=0,l=0 dp_prev[0].assign(1, 1); // 从 pos=1 … K 逐步 DP for(int pos = 1; pos <= K; pos++){ int fL = forbiddenLen[pos]; // 当前位置允许的最小禁止长度 // dp_cur[t] 里,l 最多也只需要到 pos for(int t = 0; t <= pos; t++){ dp_cur[t].assign(pos+1, 0); } // 先预算 dp_prev 各行的前缀和(全 l 的和) static int sum_prev[1505]; for(int t = 0; t <= pos-1; t++){ i64 s = 0; for(int l = 0; l < (int)dp_prev[t].size(); l++){ s += dp_prev[t][l]; } sum_prev[t] = int(s % MOD); } // 转移:当前位置标 0(不缺失)→ 连续缺失 l 归 0 for(int t = 0; t <= pos-1; t++){ dp_cur[t][0] = add(dp_cur[t][0], sum_prev[t]); } // 当前位置标 1(缺失)→ 连续缺失 l+1,但必须 l+1 < fL for(int t = 0; t <= pos-1; t++){ int lim = min(fL-1, (int)dp_prev[t].size()); for(int l = 0; l < lim; l++){ // dp_prev[t][l] 贡献给 dp_cur[t+1][l+1] dp_cur[t+1][l+1] = add(dp_cur[t+1][l+1], dp_prev[t][l]); } } // swap dp_prev.swap(dp_cur); } // 统计 N_t:缺失集合恰好大小 t 的可行方案数 vector<int> N_t(K+1, 0); for(int t = 0; t <= K; t++){ i64 s = 0; for(int l = 0; l < (int)dp_prev[t].size(); l++){ s += dp_prev[t][l]; } N_t[t] = int(s % MOD); } // 预计算 pow_jN[j] = j^N mod vector<int> pow_jN(K+1, 1); for(int j = 1; j <= K; j++){ pow_jN[j] = mod_pow(j, N); } // 预计算二项式系数 C[k][j] static int C[1505][1505]; for(int i = 0; i <= K; i++){ C[i][0] = C[i][i] = 1; for(int j = 1; j < i; j++){ C[i][j] = add(C[i-1][j-1], C[i-1][j]); } } // 计算 P[k] = k! * S(N,k) = ∑_{j=0..k} (-1)^{k-j} C(k,j) * j^N vector<int> P(K+1, 0); for(int k = 0; k <= K; k++){ i64 s = 0; for(int j = 0; j <= k; j++){ i64 term = 1LL * C[k][j] * pow_jN[j] % MOD; if((k-j)&1) s = (s - term) % MOD; else s = (s + term) % MOD; } P[k] = int((s + MOD) % MOD); } // 最终答案:ans = ∑_{t=0..K} N_t * P[K - t] i64 ans = 0; for(int t = 0; t <= K; t++){ ans = (ans + 1LL * N_t[t] * P[K - t]) % MOD; } cout << (ans + MOD) % MOD << "\n"; return 0; }