P7710 [Ynoi2077] stdmxeypz 题解
DFS 序分块,变成区间深度模 $x$ 等于 $y$ 的点加 $z$。散块暴力,考虑整块。
对 $x$ 根号分治。$x\le\sqrt n$ 时,维护 $X_{i,j,k}$ 表示 $i$ 块被形如模 $j$ 等于 $k$ 的修改加了多少即可。
$x>\sqrt n$ 时维护 $Y_{i,j}$ 表示 $i$ 块内深度为 $j$ 的点被 $x>\sqrt n$ 的修改加了多少,
枚举此次操作影响到的深度 $d$,则需要对 $Y_{[l,r],d}$ 区间加 $z$,在第一维上差分即可。
空间复杂度 $O(n\sqrt n)$,调一调块长和阈值。
#include <cstdio>
#include <algorithm>
#define B 1500
#define K 450
using namespace std;
struct E
{
int v, t;
} e[300050];
int n, m, c, z, L[350], R[350], a[300050], t[300050], b[300050], k[300050], d[300050], s[300050], h[300050], X[201][451][451], Y[300050][201];
void A(int u, int v)
{
e[++c] = {v, h[u]};
h[u] = c;
}
void D(int u)
{
s[k[b[u] = ++z] = u] = 1;
for (int i = h[u], v; i; i = e[i].t)
d[v = e[i].v] = d[u] + 1, D(v), s[u] += s[v];
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i)
t[i] = (i - 1) / B + 1;
for (int i = 1; i <= t[n]; ++i)
L[i] = (i - 1) * B + 1, R[i] = min(L[i] + B - 1, n);
for (int i = 2, x; i <= n; ++i)
scanf("%d", &x), A(x, i);
D(1);
for (int i = 0, o, v, x, y, z; i < m; ++i)
{
scanf("%d%d", &o, &v);
if (o & 1)
{
scanf("%d%d%d", &x, &y, &z);
int l = b[v], r = b[v] + s[v] - 1;
if (t[l] == t[r])
{
for (int i = l; i <= r; ++i)
if ((d[k[i]] - d[v]) % x == y)
a[k[i]] += z;
continue;
}
else
{
for (int i = l; i <= R[t[l]]; ++i)
if ((d[k[i]] - d[v]) % x == y)
a[k[i]] += z;
for (int i = L[t[r]]; i <= r; ++i)
if ((d[k[i]] - d[v]) % x == y)
a[k[i]] += z;
if (x <= K)
for (int i = t[l] + 1; i < t[r]; ++i)
X[i][x][(d[v] + y) % x] += z;
else
for (int i = d[v] + y; i < n; i += x)
Y[i][t[l] + 1] += z, Y[i][t[r]] -= z;
}
}
else
{
int q = a[v];
for (int i = 1; i <= K; ++i)
q += X[t[b[v]]][i][d[v] % i];
for (int i = 1; i <= t[b[v]]; ++i)
q += Y[d[v]][i];
printf("%d\n", q);
}
}
return 0;
}