题解 - AT_arc212_c ABS Ball
AT_arc212_c [ARC212C] ABS Ball
难度:青(提高)
题目描述
有 \(N\) 个白球。首先,你需要将每个球涂成红色或蓝色。
然后,你将这 \(N\) 个已被涂成红色或蓝色的球放入 \(M\) 个可区分的盒子之中。
设 \(a_i\) 和 \(b_i\) 分别表示第 \(i\) 个盒子中红球和蓝球的数量。
请你求出所有放球方式中,\(\prod_{1\leq i \le M}|a_i-b_i|\) 的和,对 \(998244353\) 取模。
这里,两种放球方式不同,当且仅当存在某个 \(i\),使得 \(a_i\) 或 \(b_i\) 不同。
特别地,球互相之间不区分。
输入格式
输入从标准输入读取,格式如下:
\(N\) \(M\)
输出格式
输出答案。
输入输出样例 #1
输入 #1
2 1
输出 #1
4
输入输出样例 #2
输入 #2
5 7
输出 #2
0
输入输出样例 #3
输入 #3
10000000 5000000
输出 #3
965172629
说明/提示
样例解释 1
有三种方式将球放入盒子 1 中。
如果你放入一个红球和一个蓝球,\(|a_1-b_1|=0\)。
如果你放入两个红球或两个蓝球,\(|a_1-b_1|=2\)。
所以答案为 \(0+2+2=4\)。
数据范围
- \(1 \leq N,M \leq 10^7\)
- 所有输入值均为整数。
由 ChatGPT 5 翻译
模拟赛时
\(O(N^4)\) DP做法骗了 42pts 然后就没思路了,想不出计数的冗余项在哪里。
思路
赛后听同学讲解思路,第一步是发现对于任意的小球的排列,都可以让红蓝两两抵消,然后剩下每个箱子装 \(x_i\) 个白球,然后此方案的计数直接等于 \(\prod_{i=1}^{M}x_i\)。
现在问题转化为外层枚举总共有 \(p\) 对红蓝球,然后它们分配给 \(M\) 个不同的箱子就是 \(\binom{M+p}{p}\) 种方法。内层需要 \(O(1)\) 计算剩下 \(N-2p\) 个白球放进 $M $ 个箱子之后的价值之和。
考虑怎么快速计算这个东西。2026年9月1日13:17:40 刚才又去咨询了一下大佬,首先我们先考虑可以有空的插板,就是 \(\binom{N-2p+M}{M-1}\) 种方案,我们再考虑刚才的估值函数 \(\prod_{i=1}^Mx_i\),这是不是就等价于“从每个箱子里挑一个白球出来”的方案数?你可能会问,这样做不还是要枚举每个箱子里有多少个球吗?
A change in perspective is worth 80 IQ points.
-------- Alan Kay
这个时候我们考虑先选出 \(2M-1\) 个球,再从其中选出 \(M-1\) 个作为板子,其组合意义和上面是完全等价的。而这个非常好算,就是 \(\binom{N-2p+M}{2M-1}\),显然我们保证 \(N-2p\ge M\) 来确保贡献非 \(0\) 即可,或者直接把它的组合数干成 \(0\) 就可以了。记得最后我们发现每个箱子里白球可以统一染色成红色或蓝色,故乘以 \(2^m\)
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int MOD = 998244353;
const int INF = 2e9; // const ll INF = 4e18
const int N = 1e7 + 5, TOT = 2e7 + 5;
ll qpow(ll a, ll b) {
ll ret = 1;
while (b) {
if (b & 1) ret = ret * a % MOD;
a = a * a % MOD;
b >>= 1;
}
return ret;
}
ll frac[TOT], rev[TOT];
int n, m;
ll C(int n, int m) {
if (m > n) return 0ll;
ll ret = frac[n] * rev[n - m] % MOD;
ret = ret * rev[m] % MOD;
return ret;
}
ll ans = 0;
int main()
{
// ios::sync_with_stdio(0);
// cin.tie(0);
frac[0] = 1;
for (ll i = 1; i < TOT; ++i) {
frac[i] = frac[i - 1] * i % MOD;
}
rev[TOT - 1] = qpow(frac[TOT - 1], MOD - 2);
for (ll i = TOT - 1; i; --i) {
rev[i - 1] = rev[i] * i % MOD;
}
cin >> n >> m;
for (int p = 0; (p << 1) < n; ++p) {
if (n - (p << 1) < m) break;
ll tmp = C(m + p - 1, p);
(ans += C(n - (p << 1) + m - 1, (m << 1) - 1) * tmp % MOD) %= MOD;
}
ans = ans * qpow(2, m) % MOD;
cout << ans << '\n';
return 0;
}
细节好多 QwQ

浙公网安备 33010602011771号