Loading

P8353 [SDOI/SXOI2022] 无处存储

存下每个点的父亲信息 \(fa\) 和 点权 \(w\) 就已经用去近 \(54 \text{ MiB}\) 了,树剖似得彻彻底底。

考虑树分块:随机选定 \(\sqrt n\) 个点作为关键点建虚树,这样每个点向上走到关键点的步数期望为 \(\sqrt n\),然后每个关键点存原树上从它到它虚树上的父亲结点的信息。

dfs 似了,因为它占栈空间,跑一遍就 MLE,那怎么建虚树?

可以枚举 \(\sqrt n\) 个关键点,每个点打标记并往上跳,遇到打过标记的点就连边然后终止,时间复杂度 \(\mathcal O(n)\)

还有一个细节是用数组存标记会爆空间,bitset!

修改和查询可以用树上差分的思路拆分成四个,这样就只用考虑起始段的特殊情况了。

需要支持的操作:找到找到包含当前点的块、求两个点的 LCA。

  • 可以对每个关键点存他往任意一个儿子方向的下一个关键点,每次向上跳再跳下去就好了,时间复杂度 \(\mathcal O(\sqrt n)\)
  • 找 LCA 的时候类似于倍增,只不过是跳块罢了。总体细节不少的,难写呃呃呃呃呃呃呃呃啊啊啊啊啊啊啊啊啊!!

总时间复杂度是 \(\mathcal O(q \sqrt n)\),人傻常数大。

#include <bits/stdc++.h>

using namespace std;

constexpr int N = 7e6 + 10, SN = 6e3;

namespace INPUT_SPACE{
	const int S=(1<<20)+5;char B[S],*H,*T;inline int gc() { if(H==T) T=(H=B)+fread(B,1,S,stdin);return (H==T)?EOF:*H++; }
	inline unsigned int inn() { unsigned int x,ch;while((ch=gc())<'0'||ch>'9');x=ch^'0';while((ch=gc())>='0'&&ch<='9') x=x*10+(ch^'0');return x; }
}using INPUT_SPACE::inn;

int n, m, q, fa[N], anc[N], p[SN];
uint w[N], d[SN], s[SN], add[SN];
mt19937 rnd(19260817);
bitset<N> key, vis;
vector<uint> G[SN], H[SN];
unordered_map<uint, uint> id, son[SN];

inline int dep(int u) {
    int res = 0;
    while (!key[u]) u = fa[u], res++;
    return res + d[id[u]];
}

inline int getbel(int u) {
    if (key[u]) return id[u];
    while (!key[fa[u]]) u = fa[u];
    return son[id[fa[u]]][u];
}

inline void pushdown(int x, int y) {
    if (!add[x]) return;
    for (int i = p[x]; i != p[y]; i = fa[i]) w[i] += add[x];
    add[x] = 0;
}

inline void upd(int u, uint c) {
    if (!u) return;
    while (!vis[u]) w[u] += c, u = fa[u];
    if (!key[u]) {
        int down = getbel(u), up = anc[down];
        pushdown(down, up);
        for (int i = u; i != p[up]; i = fa[i]) w[i] += c, s[down] += c;
        u = up;
    } else u = id[u];
    while (u) s[u] += c * (d[u] - d[anc[u]]), add[u] += c, u = anc[u];
}

inline uint qry(int u) {
    if (!u) return 0;
    uint res = 0;
    while (!vis[u]) res += w[u], u = fa[u];
    if (!key[u]) {
        int down = getbel(u), up = anc[down];
        pushdown(down, up);
        for (int i = u; i != p[up]; i = fa[i]) res += w[i];
        u = up;
    } else u = id[u];
    while (u) res += s[u], u = anc[u];
    return res;
}

inline int keyLCA(int u, int v) {
    assert(u != 0 && v != 0);
    while (u != v) d[u] > d[v] ? u = anc[u] : v = anc[v];
    return u;
}

inline int LCA(int u, int v) {
    int x = u, y = v, du = dep(u), dv = dep(v);
    while (!vis[x]) x = fa[x]; while (!vis[y]) y = fa[y];
    if (x == y) {
        while (u != v) du > dv ? (u = fa[u], du--) : (v = fa[v], dv--);
        return u;
    }
    u = x, v = y; x = key[x] ? id[x] : getbel(u); y = key[y] ? id[y] : getbel(y);
    if (x == y) return dep(u) < dep(v) ? u : v;
    int lca = keyLCA(x, y);
    if (lca == x) return u;
    if (lca == y) return v;
    return p[lca];
}

inline void upd(int u, int v, uint c) {
    int lca = LCA(u, v);
    upd(u, c), upd(v, c), upd(lca, -c), upd(fa[lca], -c);
}

inline uint qry(int u, int v) {
    int lca = LCA(u, v);
    return qry(u) + qry(v) - qry(lca) - qry(fa[lca]);
}

int main() {
    ios_base::sync_with_stdio(0); cin.tie(nullptr), cout.tie(nullptr);
    inn(), n = inn(), q = inn(), m = sqrt(n);
    uint A, B, C; A = inn(), B = inn(), C = inn(), w[0] = inn();
    for (int i = 1; i <= n; i++) w[i] = A * w[i - 1] * w[i - 1] + B * w[i - 1] + C;
    for (int i = 2; i <= n; i++) fa[i] = inn();
    key[p[1] = 1] = 1;
    for (int i = 2; i <= m; i++) {
        do p[i] = rnd() % n + 1;
        while (key[p[i]]);
        key[p[i]] = 1;
    }
    sort(p + 1, p + m + 1); vis[1] = 1;
    for (int i = 2, u; i <= m; i++) {
        for (u = p[i]; !vis[u]; u = fa[u]) vis[u] = 1;
        if (!key[u]) key[p[++m] = u] = 1;
    }
    sort(p + 1, p + m + 1); for (int i = 1; i <= m; i++) id[p[i]] = i;
    s[1] = w[1], d[1] = 1;
    for (int i = 2, v; i <= m; i++) {
        s[i] = w[p[i]], d[i] = 1;
        for (anc[i] = fa[p[i]], v = p[i]; !key[anc[i]]; v = anc[i], anc[i] = fa[anc[i]]) s[i] += w[anc[i]], d[i]++;
        d[i] += d[anc[i] = id[anc[i]]], son[anc[i]][v] = i;
    }
    uint op, x, y, v, lastans = 0;
    while (q--) {
        op = inn(), x = inn() ^ lastans, y = inn() ^ lastans;
        if (op) cout << (lastans = qry(x, y)) << '\n', lastans &= (1 << 20) - 1;
        else v = inn() ^ lastans, upd(x, y, v);
    }
    return 0;
}
posted @ 2024-01-31 18:25  Chy12321  阅读(9)  评论(0编辑  收藏  举报