Link.
怎么一场 edu 后三道全是 dp?
VP 时做不出来这神秘题,难道不严格大于 F?
\(Q\) 不知道是什么神秘东西,考虑钦定与每个 \(Q\) 一一对应的 \(a\) 并进行计数。
\(\mathbf{Lemma.}~\) 对于一 “互补和集合” \(Q\),存在唯一的与之对应的 \(a\),满足 \(a\) 由非零个 \(1\) 和若干个互不相同的非 \(1\) 数组成。
\(\mathbf{Proof.}~\) 随便找到一个 \(Q\) 对应的 \(a\),将 \(a\) 排序。
存在性:当 \(a_1 \neq 1\) 时,将所有 \(a_i\) 减去 \(a_1 - 1\),再加入 \((m - 1)(a_1 - 1)\) 个 \(1\),此时 \(s\) 和 \(a_i\) 都减少了 \(a_1 - 1\),新的 \(a\) 构造出的 “互补和集合” 仍为 \(Q\)。此时 \(a_1 = 1\) ,若有重复非 \(1\) 值,对于每个重复非 \(1\) 值,仅保留 \(1\) 个,剩下的全部拆成若干个 \(1\),保证和不变,由于 \(s\) 不变且出现的数的种类不变,新的 \(a\) 构造出的 “互补和集合” 仍为 \(Q\)。新的 \(a\) 即为符合条件的 \(a\)。
唯一性:考虑一符合条件的 \(a\),对其去重得到 \(b\),将 \(b\) 排序,同时记 \(Q\) 的第 \(i\) 大值为 \(Q_i\),易知 \(Q_i = s - b_i\)。考虑 \(b\) 的差分:
\(b_i - b_{i - 1} = (-b_{i - 1}) - (-b_i) = (s - b_{i - 1}) - (s - b_i) = Q_{i - 1} - Q_i\)
即 \(b\) 的差分等于 \(Q\) 的差分的相反数。对于一个固定的 \(Q\),我们可以算出 \(b\) 的差分,又 \(b_1 = 1\),\(b\) 本身是唯一确定的。接下来考虑 \(a\),显然 \(a\) 只比 \(b\) 多若干个 \(1\)(可能 \(0\) 个,因为 \(b\) 中已有一个 \(1\))。
考察 \(s\):
于是 \(s\) 唯一确定,于是往 \(b\) 不停加入 \(1\) 直到总和到达 \(s\) 即可得到 \(a\),\(a\) 唯一确定。\(\square\).
于是我们计数 \(\bf{Lemma.}\) 所述的 \(a\) 即可,考虑 dp 先求出 \(b\),再补 \(1\) 得到 \(a\)(可补 \(0\) 个)。
因为 \(\lvert Q\rvert = n\),所以 \(b\) 的长度也为 \(n\),且 \(Q_1 = s - b_1 = s - 1\le x\),于是 \(s\le x + 1\)。
设 \(f_{i,j}\) 表示前 \(i\) 个位置,和为 \(j\) 的 \(b\) 的个数。对于 \(f_{n,k}\),可以通过补 \(1\) 将 \(s\) 补成 \([k, x + 1]\) 中的任意一个值,于是答案为 \(\sum_{k = 1} ^ {x + 1} (x + 1 - k + 1)\cdot f_{n, k}\)。
考虑转移,\(b\) 单调递增很烦,于是枚举差分,使用费用提前计算的技巧处理和,容易写出转移:
滚掉第一维。发现 \(f_{i, j}\) 只比 \(f_{i, j - (n - i + 1)}\) 多一个 \(f_{i - 1, j - (n - i + 1)}\),利用这一点可以做到单次转移 \(\mathcal{O}(1)\)。
貌似时间复杂度 \(\mathcal{O}(nx)\) 过不去?但我们知道 \(\sum b_i \le s \le x + 1\),而 \(b_i\) 单调递增,所以 \(b_i \ge \frac{n(n + 1)}{2}\) 即 \(\frac{n(n + 1)}{2} \le x\),有解情况下 \(\mathcal{O}(n) = x ^ {0.5}\),于是时间复杂度 \(\mathcal{O}(nx) = \mathcal{O}(x ^ {1.5})\)。
注意特判 \(n = 1\) 情况,此时会多算一种情况 \(a = b = [1]\),此时 \(Q = \{0\}\) 不满足「集合中的每个元素都是 \(1\) 到 \(x\) 之间的整数」。代码很短。
// godmoo's code
const int N = 2e5 + 5;
const int MOD = 998244353;
int T, n, x; ll f[2][N], ans = 0;
void solve(){ans = 0;cin >> n >> x;if(n == 1) return cout << x << "\n", void();if((ll)n * (n + 1) / 2 - 1 > x) return cout << 0 << "\n", void();for(int j = 0; j <= x + 1; j++) f[0][j] = f[1][j] = 0; f[1][n] = 1;for(int i = 2; i <= n; i++) for(int j = 1; j <= x + 1; j++){f[i & 1][j] = (j < n - i + 1 ? 0 : f[i & 1][j - n + i - 1] + f[i & 1 ^ 1][j - n + i - 1]) % MOD;}for(int j = 1; j <= x + 1; j++) (ans += (x + 1 - j + 1) * f[n & 1][j]) %= MOD;cout << ans << "\n";
}
int main() {ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);for(cin >> T; T; T--) solve();cout << flush;return 0;
}