LCT的access时间复杂度的势能分析

先对树任意做轻重链剖分。
然后设势函数 w w w为同时是重边与虚边的边数。
势函数的初值是 O ( n ) O(n) O(n)的。
考虑任意一次access(x)的复杂度,与其到根的虚边数量线性相关。
这些虚边会变成实边。
其中的轻边个数是 O ( log ⁡ n ) O(\log n) O(logn),这一部分的复杂度分析完毕。
其中的重边总数就是势函数,并且每花费一的时间势函数会相应减小,因此时间复杂度是势函数总和。

接下来分析势函数的变化。
考虑重边变成虚边的次数,要想总势能变化,必然有一条与他相同父亲的轻边变成实边。
而轻边变成实边的总量不会超过 O ( n log ⁡ n ) O(n \log n) O(nlogn)。因此势能总和也不会超过 O ( n log ⁡ n ) O(n \log n) O(nlogn)

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是一个简单的 C++ LCT 实现,包含了 LCT 基本操作: ```cpp #include <bits/stdc++.h> using namespace std; const int N = 1e5 + 5; // 节点结构体 struct Node { int ch[2], fa, rev; int val, sum; } tr[N]; // 判断节点x是否是其父节点的左儿子 bool is_left(int x) { return tr[tr[x].fa].ch[0] == x; } // 反转标记 void reverse(int x) { swap(tr[x].ch[0], tr[x].ch[1]); tr[x].rev ^= 1; } // 维护反转标记 void pushdown(int x) { if (tr[x].rev) { reverse(tr[x].ch[0]); reverse(tr[x].ch[1]); tr[x].rev = 0; } } // 维护sum值 void pushup(int x) { tr[x].sum = tr[tr[x].ch[0]].sum + tr[tr[x].ch[1]].sum + tr[x].val; } // 旋转操作 void rotate(int x) { int y = tr[x].fa, z = tr[y].fa; int k = is_left(x), w = tr[x].ch[k ^ 1]; tr[y].ch[k] = w; tr[w].fa = y; tr[z].ch[is_left(y)] = x; tr[x].fa = z; tr[x].ch[k ^ 1] = y; tr[y].fa = x; pushup(y); pushup(x); } // 递归下传反转标记 void splay(int x) { static int stk[N]; int top = 0, y = x; stk[++top] = y; while (!stk[top]) { y = tr[y].fa; stk[++top] = y; } while (top) pushdown(stk[top--]); while (is_left(x)) { int y = tr[x].fa, z = tr[y].fa; if (is_left(y)) rotate(y); rotate(x); } while (!is_left(x)) { int y = tr[x].fa, z = tr[y].fa; if (!is_left(y)) rotate(y); rotate(x); } } // 将x节点到根节点的路径变为一条链 void access(int x) { for (int y = 0; x; y = x, x = tr[x].fa) { splay(x); tr[x].ch[1] = y; pushup(x); } } // 将x节点所在的树变为splay树的根节点 void make_root(int x) { access(x); splay(x); reverse(x); } // 查询x节点所在的树的根节点 int find_root(int x) { access(x); splay(x); while (tr[x].ch[0]) { pushdown(x); x = tr[x].ch[0]; } splay(x); return x; } // 连接x和y两个节点 void link(int x, int y) { make_root(x); tr[x].fa = y; } // 断开x和y两个节点 void cut(int x, int y) { make_root(x); if (find_root(y) == x && tr[y].fa == x && !tr[y].ch[0]) { tr[y].fa = tr[x].ch[1] = 0; pushup(x); } } // 修改x节点的值为val void modify(int x, int val) { splay(x); tr[x].val = val; pushup(x); } // 查询x节点到y节点路径上的所有节点的权值和 int query(int x, int y) { make_root(x); access(y); splay(y); return tr[y].sum; } int main() { int n, m; scanf("%d%d", &n, &m); for (int i = 1; i <= n; ++i) { scanf("%d", &tr[i].val); tr[i].sum = tr[i].val; } while (m--) { int opt, x, y; scanf("%d%d%d", &opt, &x, &y); if (opt == 0) printf("%d\n", query(x, y)); else if (opt == 1) link(x, y); else if (opt == 2) cut(x, y); else if (opt == 3) modify(x, y); } return 0; } ``` 这里只是一个简单的 LCT 实现,对于一些细节和优化并没有处理。如果需要了解更多关于 LCT 的内容,可以参考一些经典题目,如 P3834、P3836、P4219 等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值