[JOIST 2022] 洒水器 / Sprinkler
\(d\) 很小,那大概率要枚举一些 \(\mathcal{O}(d)\) 的量。考虑到 \(u\) 的 \(d\) 级领域的点集:
记 \(u\) 的 \(k\) 级祖先为 \(f_{u, k}\),\(u\) 的 \(k\) 级后代组成的集合为 \(g_{u, k}\),那么 \(u\) 的 \(d\) 级领域的点集就是
\[g_{u, d} \bigcup g_{u, d - 1} \bigcup g_{f_{u, 1}, d - 1} \bigcup g_{f_{u, 1}, d - 2} \bigcup \dots \bigcup g_{f_{u, k}, d - k} \bigcup g_{f_{u, k}, d - k - 1} \bigcup (\bigcup\limits_{i = 0}^{d - 2} g_{1, i})
\]
即所有 \(k\) 级祖先的第 \(d - k\) 和第 \(d - k - 1\) 层后代的集合,以及一号节点的所有 \(d\) 级后代。
因为与 \(u\) 距离为 \(d\) 且两点 \(lca\) 为 \(f_{u, k}\) 的点,到 \(f_{u, k}\) 的距离为 \(d - k\),所以 \(g_{u, i}, i < d - 2\) 都会被祖先覆盖,但是根节点要特殊考虑。
namespace Loop1st {
int n, mod, Q, fa[N];
ll tag[N][41], a[N];
vector<int>e[N];
void dfs(int u) {
for (int v : e[u]) if (v != fa[u]) fa[v] = u, dfs(v);
}
void Main(int tc) {
cin >> n >> mod;
for (int i = 1, u, v; i < n; i++) {
cin >> u >> v;
e[u].push_back(v); e[v].push_back(u);
}
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++)
for (int j = 0; j <= 40; j++) tag[i][j] = 1;
dfs(1);
cin >> Q;
while (Q--) {
int op, u, d; ll v; cin >> op >> u;
if (op == 1) {
cin >> d >> v;
while (d >= 0 && u) {
tag[u][d] = tag[u][d] * v % mod;
if (d) tag[u][d - 1] = tag[u][d - 1] * v % mod;
if (u == 1) {
for (int i = d - 2; i >= 0; i--) tag[u][i] = tag[u][i] * v % mod;
}
u = fa[u]; d--;
}
} else {
ll ans = a[u];
d = 0;
while (d <= 40 && u) {
ans = ans * tag[u][d] % mod;
u = fa[u]; d++;
}
cout << ans << '\n';
}
}
}
}
做完这题再去看 QOJ9373 就会比较自然。

浙公网安备 33010602011771号