1 条题解
-
0
1967C - 树状数组
一、题意与图理解
题目定义:
- 对数组 , 是其 Fenwick Tree 表示,其中$$s_u = \sum_{v \in \text{subtree}(u)} a_v \mod 998244353 $$即每个节点 存储其对应区间内 的和。
- 表示对 连续执行 次 操作。
- 已知 ,求原数组 。
结合图:每个 只出现在它所有祖先节点的子树中,因此 会被加到所有这些祖先节点上。
二、标程核心数学结论
1. 关键系数公式
设:
- 为任意节点, 是 的祖先节点;
- 为 到 的深度差。
经过 次 操作后, 在 中的系数为:
直观理解
每次 操作,一个节点的值会“向上传播”到所有祖先节点。这等价于: 从 到 需要向上走 步,经过 次操作,总共相当于把 个“增量”分配到这 步上,其方案数就是组合数 。
三、标程给出的算法思路
1. 基础性质:叶子节点直接相等
对于 Fenwick Tree 的叶子节点(即编号为奇数的节点),其在树中没有子节点(除自身外),因此:
所以,所有叶子节点的 值可以直接由 得到。
2. 按节点顺序递推修正
标程核心做法:
- 按节点编号从小到大遍历(即从下往上);
- 对于当前节点 ,此时它的所有子节点(编号更小)的 值都已经求出;
- 对于 的所有祖先节点 ,用已知的 去修正 :$$b_v \leftarrow b_v - a_u \times \binom{\Delta d + k - 1}{\Delta d} \pmod{998244353} $$
- 处理完所有子节点后,当前节点 的 就等于 ,直接赋值。
3. 时间复杂度
Fenwick Tree 的高度为 ,每个节点最多有 个祖先,因此总复杂度为:
完全满足题目 的限制。
四、关键预处理:组合数计算
我们需要计算 。 设 ,则:
$$\binom{d + k - 1}{d} = \frac{(k)(k+1)\cdots(k+d-1)}{d!} \mod 998244353 $$由于 的最大值为树高 ,我们可以对每个节点 预先计算它到所有祖先的 ,并在 时间内算出对应的组合数。
五、C++ 标程级实现代码
#include <bits/stdc++.h> using namespace std; using ll = long long; const int MOD = 998244353; const int MAXN = 2e5 + 5; const int LOG = 20; // log2(2e5) ≈ 18 ll inv_fact[LOG]; 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 precompute() { inv_fact[0] = 1; for (int i = 1; i < LOG; i++) inv_fact[i] = qpow(i, MOD - 2); } ll comb(ll k, int d) { if (d == 0) return 1; ll res = 1; for (int i = 0; i < d; i++) res = res * ((k + i) % MOD) % MOD; for (int i = 1; i <= d; i++) res = res * inv_fact[i] % MOD; return res; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); precompute(); int t; cin >> t; while (t--) { int n; ll k; cin >> n >> k; vector<ll> b(n + 1); for (int i = 1; i <= n; i++) cin >> b[i]; vector<ll> a(n + 1); for (int u = 1; u <= n; u++) { // 计算u到其所有祖先的组合数,并修正b int cur = u; int d = 0; while (cur <= n) { ll c = comb(k, d); if (cur != u) { b[cur] = (b[cur] - a[u] * c % MOD + MOD) % MOD; } cur += cur & -cur; d++; } a[u] = b[u]; } for (int i = 1; i <= n; i++) cout << a[i] << " "; cout << "\n"; } return 0; }
六、正确性保证
- 按从小到大顺序处理,保证处理节点 时,其所有子节点的 值都已求出;
- 用组合数修正所有祖先节点后,
b[u]就不再包含任何子节点的贡献,直接等于a[u]; - 时间复杂度 ,可以通过所有测试点。
- 1
信息
- ID
- 7067
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者