首页 > 其他分享 >P8353 [SDOI/SXOI2022] 无处存储

P8353 [SDOI/SXOI2022] 无处存储

时间:2024-01-31 18:34:14浏览次数:23  
标签:return SDOI SXOI2022 int P8353 fa while lca inline

存下每个点的父亲信息 \(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;
}

标签:return,SDOI,SXOI2022,int,P8353,fa,while,lca,inline
From: https://www.cnblogs.com/chy12321/p/17999883

相关文章

  • P8352 [SDOI/SXOI2022] 小 N 的独立集
    还是先打暴力,枚举\(k^n\)种树的形态做树形DP,时间复杂度\(\mathcalO(nk^n)\),拿下\(n\le8\)和\(k=1\)的\(25\)分。虽然很简单,还是交代下吧:设\(f(u,0/1)\)表示以\(u\)为根的子树中是否选\(u\)的最大权独立集,\(f(u,0)\getsw_u+\sum\limits_{v\inson_u}......
  • P8350 [SDOI/SXOI2022] 进制转换
    记\(len_x\)为\(x\)在\(a\)中出现的次数,显然有\(\mathcalO(nq)\)的暴力,拿下\(20\)分。感觉用数据结构难以维护,考虑根号做法。根号做法有一个阈值\(L\),然后分讨(钦定\(len_x\lelen_y\)):\(len_x\lelen_y\leL\)直接暴力做,时间复杂度\(\mathcalO(L)\)。\(L......
  • [SDOI2019] 移动金币(阶梯博弈)
    题面一枚金币向左移若干格等价若干个空格向右移一个金币,终状态为所有空格在最右,转换为阶梯博弈阶梯博弈每个阶梯上有若干枚石子,每次操作将同个阶梯的任意石子向下移一个阶梯,不能操作者输等价对奇数阶梯做Nim博弈先手按Nim博弈操作。若对方移动偶数阶梯,则将对方移动石子继续下......
  • P5618 SDOI2015 道路修建题解
    题目分析虽然数据范围只有\(n\le60000\),但是完全可以直接用线段树做。首先考虑那种状态的图在左边和右边加入节点和边之后可以连通。容易发现,这种图有这两个性质:至少有一条路径,经过最左端和最右端中的点。所有点至少和最左端和最右端的点连通。于是可以划分成以下几种状态......
  • P5362 [SDOI2019] 连续子序列 题解--zhengjun
    题面传送门提供一种和其他题解完全不同的解法。记\(P_0\)为题中给出的序列,\(P_1\)为\(P_0\)取反的结果。记\(S_{l\simr}\)表示\(S_lS_{l+1}\dotsS_{r}\)。方便起见,\(P\)下标从\(0\)开始,其余的串都是从\(1\)开始。这里用\(P_{0,i}=\operatorname{popcount(i)}......
  • [SDOI2017] 天才黑客
    [SDOI2017]天才黑客题目背景SD0062号选手小Q同学为了偷到SDOI7012的试题,利用高超的黑客技术潜入了SDOI出题组的内联网的中央控制系统,然而这个内联网除了配备有中央控制系统,还为内联网中的每条单向网线设定了特殊的通信口令,这里通信口令是一个字符串,不同网线的口令可能不同。这......
  • 洛谷 P5359 [SDOI2019] 染色
    洛谷传送门LOJ传送门dp好题。首先有一个显然的状态,设\(f_{i,x,y}\)为第\(i\)列上下两格的颜色分别为\(x,y\)的方案数。但是这样做时间复杂度至少为\(O(nm^2)\),无法接受。注意到全\(0\)列的转移是重复的。我们可以试着只在两个相邻非全\(0\)列转移。这样我们需......
  • 题解 [SDOI2016] 游戏
    可以看出来出题人很想出一道把李超和别的什么东西凑起来的题目,于是给了这么一个缝合怪。https://www.luogu.com.cn/problem/P4069符号有点混乱。比如箭头又可以表示路径又可以表示赋值,代入语境应该还是好理解的。看到\(a\timesdis+b\)就应激反应出来是李超了,看到\(s\to......
  • [SDOI2010] 大陆争霸
    [SDOI2010]大陆争霸屁话真多。第一眼看上去好像是最短路加了个强制拓扑。也就是说当结界还没被破坏的时候,已经到达的机器人只能干等着。在dijkstra中,机器人所在的点可以更新最短路。但拓扑图上该点的入度不为\(0\),即结界产生器没有被全部破坏时,不能入队。当炸掉一个结界......
  • [SDOI2010] 大陆争霸 题解
    [题目传送门](https://www.luogu.com.cn/problem/P2446)#解法由题可知,一个城市$u$保护城市$v$,所以建一条边$u\tov$表示城市$u$保护城市$v$,因为题目说保证有解,所以建的图一定是一个**有向无环图$DAG$**。再在此基础上求出最短路径。具体过程为设$dis_u$表示实际到达(攻破)$u$的最......