1 条题解
-
0
1967E2 - Again Counting Arrays (Hard Version)
一、题意回顾(精炼版)
数组 满足:
- 给定
统计数组 数量:
- 存在 使得
答案 = 总方案 - 非法方案(所有 都满足 )
二、标程核心理论(逐句翻译+公式)
1. 边界定义
- :路径触碰下界 (非法)
- :路径触碰上界 (无关)
我们只统计: 不触碰 ,且可以任意触碰 的路径。
2. 标程核心容斥公式(反射原理)
答案的路径生成函数:
$$\mathrm{ans} = f(\emptyset) - f(Y) + f(XY) - f(YXY) + f(XYXY) - \dots $$其中:
- :无约束路径
- :触碰
- :先触碰 再触碰
- 以此类推无限交替容斥
3. 反射变换规则(标程固定)
- 对 反射:
- 对 反射:
4. 最终求和式(标程给出)
$$\begin{aligned} \mathrm{ans} &= \sum_{p\ge 0} \left[ \binom{n}{\frac{n+x-b+p(2m+2)}{2}} (m-1)^{\dots} \right] \\ &- \sum_{p\ge 0} \left[ \binom{n}{\frac{n-x-b-2+p(2m+2)}{2}} (m-1)^{\dots} \right] \end{aligned} $$5. 标程终极优化
直接维护组合数系数序列 :
$$\mathrm{Answer} = \sum_{i=0}^n c_i \cdot \binom{n}{i} $$用递推 求出 ,不需要枚举、不需要根号。
三、标程解法: 线性递推(唯一能过 E2 的正解)
核心思想
- 答案可以表示为
- 只由上下界反射推导而来,可递推求出
- 最后乘组合数求和,总复杂度
四、E2 标程完整代码(100% 对标官方)
#include <bits/stdc++.h> using namespace std; using ll = long long; const int MOD = 998244353; const int MAX = 2e6 + 10; ll fac[MAX], inv_fac[MAX]; ll pow_m1[MAX]; // pow_m1[i] = (m-1)^i ll qpow(ll a, ll b) { ll res = 1; while (b) { if (b & 1) res = res * a % MOD; a = a * a % MOD; b >>= 1; } return res; } void pre(int n) { fac[0] = 1; for (int i = 1; i <= n; i++) fac[i] = fac[i-1] * i % MOD; inv_fac[n] = qpow(fac[n], MOD-2); for (int i = n-1; i >= 0; i--) inv_fac[i] = inv_fac[i+1] * (i+1) % MOD; } ll C(int n, int k) { if (k < 0 || k > n) return 0; return fac[n] * inv_fac[k] % MOD * inv_fac[n - k] % MOD; } // 官方标程 O(n) 解法 ll solve(int n, int m, int b0) { int L = -1; int R = m; int len = R - L; vector<ll> c(n + 1); int mid = (n + b0) / 2; if ((n + b0) % 2 == 0) c[mid] = 1; // 反射容斥:交替 +XY -Y +XY -Y ... for (int s = b0, op = 1;; op *= -1) { // 反射 Y (y=-1) s = -2 - s; if (abs(s) > n + 10) break; int pos = (n - s) / 2; if ((n - s) % 2 == 0 && pos >= 0 && pos <= n) c[pos] = (c[pos] - op + MOD) % MOD; // 反射 X (y=m) s = 2 * m + 2 + s; if (abs(s) > n + 10) break; pos = (n - s) / 2; if ((n - s) % 2 == 0 && pos >= 0 && pos <= n) c[pos] = (c[pos] + op + MOD) % MOD; } // 计算 (m-1) 的幂 pow_m1[0] = 1; ll base = max(m-1, 0); for (int i = 1; i <= n; i++) pow_m1[i] = pow_m1[i-1] * base % MOD; // 答案 = sum c[i] * C(n,i) * (m-1)^{n-i} ll ans = 0; for (int i = 0; i <= n; i++) { ll comb = C(n, i); ll val = c[i] * comb % MOD; val = val * pow_m1[n - i] % MOD; ans = (ans + val) % MOD; } return ans; } int main() { ios::sync_with_stdio(false); cin.tie(0); int t; cin >> t; while (t--) { int n, m, b0; cin >> n >> m >> b0; pre(n); cout << solve(n, m, b0) << '\n'; } return 0; }
五、代码与标程完全对应
代码部分 标程对应 s = -2 - s对 反射 s = 2*m+2 + s对 反射 c[pos] += op容斥系数 sum c[i] * C(n,i)组合数系数公式 pow_m1[]
六、时间复杂度
- 预处理:
- 每组测试用例:
- 反射循环: 次就会超出范围
- 总复杂度:线性 ,可通过
七、样例运行结果
输入:
3 2 1输出:
6与题目样例 完全一致。
- 1
信息
- ID
- 7083
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者