这是一份 \(\rm LCT\) 入门总结。

关于 \(\rm LCT\) 的复杂度这里不会提及,只会记录 \(\rm LCT\) 的基本操作和经典例题,但神奇的 \(\rm LCT\) 虽然常数巨大但还是 \(O(n \log n)\) 的优秀复杂度。

UPD on 2021.7.1 : 复杂度证明可以参考 这里

\(\rm Link-Cut-Tree\) 又名动态树,顾名思义他能支持动态维护树的形态即支持加边删边,那么这样一个神仙数据结构是怎样工作呢?

首先类似于树剖的思想,我们将原树剖分成链在链上做序列操作,但不同于树剖的是我们 \(\rm LCT\) 是随意认一个儿子连成链,下面称这个儿子为实儿子,并且这个儿子可能随意更改。具体的说我们会将一棵树剖分成一些链,下面我们成这条链为实链,如下图所示(感谢 \(\rm FlashHu\) 的图片)

可以看到在原树上我们向他的实儿子连一条实边即上图加粗的边,向他的虚儿子连一条虚边即上图中画成虚线的边。对于实边连成的一条链我们以深度为权值将其放入一颗 \(\rm Splay\) 中维护,并且对于每个点我们将他的儿子记作在 \(\rm Splay\) 中的左右儿子。那么对于每个点的父亲,如果该点不是其所在 \(\rm Splay\) 的根,那么他的父亲将是在 \(\rm Splay\) 中的父亲,否则他的父亲将是他所在实链顶端的父亲。根据这些定义我们可以将上图画成用 \(\rm Splay\) 维护之后的形态,如下图所示。

搞懂了这些定义之后我们就可以来看一些操作了,可以拿出草稿纸画画图,建议直接画出原树和虚实边。

我才不会告诉你下面没图了

  • \(\rm Access(x)\)

将 \(x\) 到根节点放入到同一颗 \(\rm Splay\) 中,即打通 \(x\) 到根节点的路径,这也是 \(\rm LCT\) 最为核心的操作。首先我们 \(\rm Splay(x)\) 将 \(x\) 旋到当前 \(\rm Splay\) 的根,那么这时候 \(x\) 的父亲将会是 \(x\) 所在实链顶端的父亲,那么我们就应该将其父亲的实儿子换成 \(x\) 即可,为了保持 \(\rm Splay\) 的形态,我们 \(\rm Splay(fa_x)\),那么此时 \(fa_x\) 的右儿子将会是所有深度比 \(fa_x\) 大的所在实链中的节点,即 \(fa_x\) 所在实链在 \(fa_x\) 下面的一部分,这时候我们将 \(fa_x\) 的右儿子改成 \(x\),我们重复这个操作知道 \(x\) 没有父亲为止。

  • \(\rm Makeroot(x)\)

很骚的一个操作,我们可以使用他将原树的根换为 \(x\)。首先根据前面定义的东西可以发现 \(LCT\) 并没有直接维护原树中的深度,而是通过 \(\rm Splay\) 维护其相对深度关系。那么不难发现我们将原树根换为 \(x\) 后,只有 \(x\) 到根的这条路径上点的相对深度关系会改变,并且恰好是将深度关系翻转过来了,我们知道 \(\rm Splay\) 是可以做区间翻转操作的,于是这个操作就可以解决了。具体地我们首先 \(\rm Access(x)\) 将 \(x\) 到根的路径放到一颗 \(\rm Splay\) 中,为了方便起见我们 \(\rm Splay(x)\) 将 \(x\) 旋到当前 \(\rm Splay\) 的根,接着只需要在 \(x\) 这个位置上打上区间翻转标记即可。

  • \(\rm Split(x, y)\)

将 \(x, y\) 这条路径放到同一个 \(\rm Splay\) 中,有了前面的两个操作,这个操作就不难了,我们首先 \(\rm Makeroot(x)\),然后 \(\rm Access(y)\) 即可。为了方便后面的操作我们再 \(\rm Splay(y)\).

  • \(\rm Findroot(x)\)

找到 \(x\) 所在原树中的树根。首先我们 \(\rm Access(x)\),接着 \(\rm Splay(x)\) 那么 \(x\) 所在实链最浅的节点即为原树的根,那么我们只需要不断查找 \(x\) 的左子树即可。

  • \(\rm Link(x, y)\)

在 \(x, y\) 中连一条边。终于到了 \(\rm LCT\) 支持的操作了。有一些毒瘤题可能 \(x, y\) 已经联通如果,我们在 \(x, y\) 之间直接连边那么这张图就不再会是一颗树,就会破坏 \(\rm LCT\) 的形态,为了避免这种情况我们必须要判断 \(x, y\) 的联通性。类似于并查集的思想我们首先 \(\rm Makeroot(x)\),接下来只需要判断 \(\rm Findroot(y) \ne x\) 即可。如果 \(x, y\) 未联通,在我们执行完 \(\rm Makeroot(x), Findroot(y)\) 之后此时 \(x\) 已经是所在 \(\rm Splay\) 的根,且 \(x\) 是 \(\rm Splay\) 中深度最小的点,那么我们直接令 \(fa_x = y\) 即可。

  • \(\rm Cut(x, y)\)

删除 \(x, y\) 之间的边。同样这个操作可能在某些题中也会不合法,因此我们要判断删边的合法性。首先我们需要判断 \(x, y\) 的联通性,像 \(\rm Link(x, y)\) 中的判断那样,首先 \(\rm Makeroot(x)\) 再判断 \(\rm Findroot(y) = x\)。同时要注意这样并不能判断 \(x, y\) 是否直接相连,如果 \(x, y\) 直接相连bugu的话那么 \(x, y\) 中将不会存在节点,即实链中不会存在深度大于 \(x\) 且小于 \(y\) 的节点,同样此时我们 \(\rm Makeroot(x), Findroot(y)\) 以后 \(x, y\) 在同一颗 \(\rm Splay\) 中且 \(y\) 为当前 \(\rm Splay\) 的根,那么我们只需判断 \(y\) 的左儿子是否为 \(x\),且 \(x\) 的右子树是否非空即可。如果删边操作合法,我们只需要把 \(fa_x, y\) 的左儿子改成 \(0\) 即可。

这就是 \(\rm LCT\) 的一些基本操作了,具体的题目 \(\rm Splay\) 中的节点维护的东西不同,但是关于 \(\rm LCT\) 我们一定要注意的一点就是因为我们有区间翻转操作,同时我们在 \(\rm rotate(x), Findroot(x)\) 的时候需要调用左右儿子信息,如果我们没有下传区间翻转标记那么我们当前调用的左右儿子的信息将会是不正确的,所以我们在 \(\rm Splay(x)\) 之前先将 \(x\) 到根的翻转标记下传,在跟普通平衡树一样 \(\rm Splay\) 即可。

下面放出【模板】Link Cut Tree 的代码及注释。

#include<bits/stdc++.h>
using namespace std;
#define N 500000 + 5
#define rep(i, l, r) for(int i = l; i <= r; ++i)
int n, m, x, y, opt, top, s[N], st[N], fa[N], val[N], tag[N], ch[N][2];
int read(){
char c; int x = 0, f = 1;
c = getchar();
while(c > '9' || c < '0'){ if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
int which(int x){
return (ch[fa[x]][1] == x);
} //查询 x 是其父亲的左儿子还是右儿子
void up(int x){
s[x] = s[ch[x][0]] ^ s[ch[x][1]] ^ val[x];
} //维护 Splay 中子树异或和
int isroot(int x){
return ((ch[fa[x]][0] != x) && (ch[fa[x]][1] != x));
} //判断 x 是否为当前 Splay 的根
void down(int x){
if(!tag[x]) return;
tag[x] = 0, tag[ch[x][0]] ^= 1, tag[ch[x][1]] ^= 1;
swap(ch[x][0], ch[x][1]);
} //下传区间翻转标记
void rotate(int x){
int y = fa[x], z = fa[y], k = which(x), w = ch[x][k ^ 1];
fa[w] = y, ch[y][k] = w;
fa[x] = z; if(!isroot(y)) ch[z][which(y)] = x; // 注意这里如果 y 为 Splay 的根那么 z 和 y 就不会在一条实链(Splay)中
fa[y] = x, ch[x][k ^ 1] = y;
up(y), up(x); //修改了父子关系,更新节点答案。
}
void Splay(int x){
int cur = x; st[++top] = x;
while(!isroot(cur)) cur = fa[cur], st[++top] = cur; //将 x 到根路径上的点压入栈
while(top) down(st[top--]); // 下传 x 到根路径上的翻转标记以便获取正确的儿子信息
while(!isroot(x)){
int y = fa[x], z = fa[y];
if(!isroot(y)){
if(which(x) == which(y)) rotate(y);
else rotate(x); //双旋
}
rotate(x);
}
}
void Access(int x){
for(int y = 0; x; y = x, x = fa[x]) Splay(x), ch[x][1] = y, up(x);
}
void Makeroot(int x){
Access(x), Splay(x), tag[x] ^= 1, down(x);
}
void Split(int x, int y){
Makeroot(x), Access(y), Splay(y);
}
int Findroot(int x){
Access(x), Splay(x);
while(ch[x][0]) x = ch[x][0], down(x);
return x;
}
void Link(int x, int y){
Makeroot(x);
if(Findroot(y) != x) fa[x] = y;
}
void Cut(int x, int y){
Makeroot(x);
if(Findroot(y) == x && ch[y][0] == x && !ch[x][1]) fa[x] = ch[y][0] = 0, up(y);
}
int main(){
n = read(), m = read();
rep(i, 1, n) val[i] = read();
rep(i, 1, m){
opt = read(), x = read(), y = read();
if(opt == 0) Split(x, y), printf("%d\n", s[y]);
if(opt == 1) Link(x, y);
if(opt == 2) Cut(x, y);
if(opt == 3) Splay(x), val[x] = y, up(x);
}
return 0;
}

看完了 \(\rm LCT\) 的一些基本操作之后让我们来看一些 \(\rm LCT\) 的经典例题。

维护链信息

这类问题一般使用和线段树一样的懒标记。

经典例题:[国家集训队]Tree II

题意:给定一颗大小为 \(n(n \le 10 ^ 5)\) 的树,支持加边删边,将一条链上的权值加上一个数,乘上一个数,求一条链上的权值和。

放到序列上来做就是 线段树2 了,首先如果没有加删边操作我们可以直接使用树剖,但加上加删边操作后我们只能考虑 \(\rm LCT\)。类似于线段树打懒标记的想法,我们也可以直接在每个节点打下加法乘法懒标记,具体来说对于一个修改 \(x, y\),我们首先 \(\rm Split(x, y)\) 直接在 \(y\) 上面打上懒标记即可,每次下传懒标记的时候先下传区间翻转标记,再像 线段树2 那样下传懒标记即可,但是这里我们并不知道区间的长度,所以我们还需要维护一个子树大小 \(s_x\).

其他的同类题目:

[HNOI2010]弹飞绵羊

[SDOI2011]染色

[SHOI2014]三叉神经树

维护联通性 / 双连通分量

维护联通性的题目比较板或者与 \(\rm LCT\) 本身没有太大关系就不放在这里了。

这类问题的一般套路是将环缩成点处理。

[AHOI2005] 航线规划

题意:给出一张 \(n(n \le 3 \times 10 ^ 4)\) 个点 \(m(m \le 10 ^ 5)\) 条边的无向图,\(q(q \le 4 \times 10 ^ 4)\) 次操作,支持删边和查询两点之间割边的数量。

不难发现本题中修改操作只有删边,那么我们可以时间倒流从后往前依次加边。不难发现如果一条链在两端之间加一条边将形成一个环,那么这个环中的任何一条边都不会是割边,那么由此我们可以将连成的环缩成一个点,那么剩下的图将会是一棵树,查询两点之间的割边数量就是两点之间的点数减 \(1\).具体来讲我们加入边 \(x, y\) 的过程中如果 \(x, y\) 不连通,那么我们直接连边 \(x, y\),如果 \(x, y\) 已经联通那么 \(x, y\) 这条路径会形成一个环,我们将 \(x, y\) 这条路径上暴力缩成一个点,因为总共修改的点是 \(n\) 的,所以暴力修改的复杂度是对的。每次查询就只需要查询 \(x, y\) 这条路径上点的个数减 \(1\) 即可。但本题并不需要这么麻烦,对于已经联通的 \(x, y\),我们将 \(x, y\) 这条路径上的权值置成 \(0\) ,对于查询只需要查询 \(x, y\) 上的权值和即可。

其他同类题目:

[SDOI2008]Cave 洞穴勘测

部落冲突

星球联盟

长跑

维护生成树

这类问题的一般套路是断掉原树上的一条边再连一条边让答案更优。

经典例题:[WC2006]水管局长

题意:给定一张有 \(n(n \le 10 ^ 5)\) 个节点和 \(m(m \le 10 ^ 5)\) 条边的无向图,\(q(q \le 10 ^ 5)\) 次操作,支持删边和找到 \(x, y\) 之间的路径是得最大的边权最小。

修改只有删边操作的话按照套路考虑时间倒流。不难发现为了让最小化 \(x, y\) 路径上的最大边权,我们只需要维护一颗最小生成树即可,由 \(\rm Kruskal\) 的流程就可说明正确性。现在我们加入一条边 \(x, y\) 如果 \(x, y\) 之间没有联通,为了保证联通性我们直接将 \(x, y\) 连一条边。如果 \(x, y\) 之间已经联通,如果保留这条边那么我们就必须删掉 \(x, y\) 路径上的一条边才能继续维护树的形态。那么为了让答案更优,我们直接查询 \(x, y\) 路径上最大的边权 \(w\),如果 \(w\) 大于当前加入边的边权,那么我们将最大边权的边删掉,将当前边连上,否则加入当前边答案不会更优,那么直接跳过即可。查询我们就只需要查询 \(x, y\) 路径上的最大边权即可。注意我们这里都是维护的边信息,跟我们原 \(\rm LCT\) 维护点信息有所不同,所以我们要想办法将边信息放到点上。但是因为我们的 \(\rm LCT\) 需要支持换根,那么我们不能跟以前树剖的套路一样将与儿子连边的边权挂到儿子上,因为根会改变,原树的父子关系也会改变。但是如果我们建立一个虚点 \(x\),将边权转化为 \(x\) 的点权,接着将当前需要连接的两个点 \(u, v, \rm Link(u, x), Link(x, v)\) 即可,然后我们删边需要删掉这两条边。

其他同类题目:

【清华集训2016】温暖会指引我们前行

[BJWC2010]严格次小生成树

最小差值生成树

[NOI2014]魔法森林

维护子树信息

这类问题的一般套路是实儿子和虚儿子分开维护。

经典例题1:[BJOI2014]大融合

题意:完成 \(q(q \le 10 ^ 5)\) 次操作,支持无向边,查询经过 \(u, v\) 的简单路径数量。

不难发现这个查询实际上是 \(u\) 为根的子树大小和除此之外 \(v\) 为根的子树大小的乘积。那么现在的问题转化为需要维护子树大小和动态加边。回顾我们 \(\rm LCT\) 的操作,我们发现我们维护的所有的信息都是所在 \(\rm Splay\) 也就是实儿子的信息,这样看来 \(\rm LCT\) 貌似不能做这个问题?不难发现我们的问题在于没有维护虚儿子的信息,那么我们不直接维护虚儿子的信息就好了?于是我们令 \(s_x\) 为以 \(x\) 为根的 \(\rm Splay\) 子树整棵树包括虚儿子的大小和,和 \(es_x\) 表示 \(x\) 的虚子树大小之和。这样一来最开始所有儿子均为虚儿子,于是可以将 \(es\) 直接初始化为原树的 \(size\),那么我们在 \(\rm Access(x)\) 切换实虚儿子的时候将 \(es\) 更改即可。最后我们查询答案首先 \(\rm Makeroot(u)\),接着 \(\rm Access(v), Splay(v)\) 输出 \(s_u \times (s_v - s_u)\) 即可。

经典例题2:QTREE5 - Query on a tree V

题意:给定一颗大小为 \(n(n \le 10 ^ 5)\) 的树,每个点开始为黑色,完成 \(q\) 次操作,支持修改某个点的颜色(黑色变白色或白色变黑色),给定 \(u\) 查询距离 \(u\) 最近的白点距离。

对于树上任意两点 \(u, v\),\(dis_{u, v} = dep_u + dep_v - 2 \times dep_{lca}\),那么当我们 \(dep_u\) 固定的时候,我们只需要找到白点中最小的 \(dep_v - 2 \times dep_{lca}\) 即可。不难发现这里的 \(lca\) 是 \(u\) 到根路径上的一个点,那么我们对每个点开一个 \(set\) 来维护该节点虚子树和自身的 \(dep_v - 2 \times dep_{lca}\),对于实链上的信息我们直接维护整颗 \(\rm Splay\) 上所有节点包括虚子树的 \(dep_v - 2 \times dep_{lca}\) 最小值,显然这样是可以向上传递信息的,每次修改我们只需要 \(\rm Access(x), Splay(x)\) 那么 \(x\) 既不会是任意一个节点的虚儿子也不会是任意一个节点的实儿子,我们直接对 \(x\) 节点进行修改是不会对答案造成影响的。每次查询我们直接 \(\rm Access(x), Splay(x)\) 直接查询 \(x\) 的答案再加上 \(dep_x\) 即可。

经典例题3:QTREE4 - Query on a tree IV

题意:给定一颗大小为 \(n(n \le 10 ^ 5)\) 的树,每个点开始为黑色,完成 \(q\) 次操作,支持修改某个点的颜色(黑色变白色或白色变黑色),查询全局最远的白色点对。

实际上上面那个题的做法加上树的直径的性质也能做掉这个题,但下面我们将提出另外一种做法,当然这种做法也能做掉上面那个题。

这种做法类似于线段树求最大子段和的分治做法,对于 \(\rm Splay\) 上的节点 \(x\) 我们递归处理出最远的包含在左子树和右子树(包括虚儿子)的点对,再处理跨过 \(x\) 的点对,显然这样是能包含所有点对的,下面我们考虑如何用子节点的答案来更新父节点。先上一张图:

假设我们当前需要更新出以 \(4\) 为根的 \(\rm Splay\) 所在原树中包含虚儿子的答案,我们令其为 \(val_x\),和其他维护子树信息的方法一样,我们将实儿子和虚儿子分开维护,对于虚儿子我们可以直接继承虚儿子的答案,或者考虑跨过 \(4\) 号点的答案,那么我们只需要找到 \(4\) 往下虚儿子的最长链和次长链即可。于是对于每个虚儿子,我们维护两个 \(set\) 一个直接维护虚儿子的答案,一个维护虚儿子中的最长链,于是虚儿子对答案的贡献我们就处理完了,下面考虑实儿子。看一看上面的图,红框内的点表示 \(4\) 所在 \(\rm Splay\) 中的左右子树,黑框内的点表示整个左右子树包括虚儿子维护的子树范围。首先我们一样可以直接继承左右儿子的答案即 \(val_x = \max(val_{ls}, val_{rs})\),类似地再考虑跨过 \(x\) 的最长链,也就是 \(x\) 能往上走到的最远白点和往下或往虚子树内走到的最远白点的距离之和。但我们只维护出了左右儿子在其维护范围内的答案,不能直接维护出 \(x\) 往上走到的最远白点和往下或往虚子树内走到的最远白点的距离。为了能够表示出 \(x\) 往上走到的最远白点和往下或往虚子树内走到的最远白点的距离,我们可以发现 \(x\) 往上走到的最远白点距离就是 \(x\) 在原树中父亲即 \(2\) 能走到的最远白点距离加上 \(x\) 到父亲的距离,而 \(x\) 在原树的父亲就是 \(x\) 在 \(\rm Splay\) 中左子树中深度最小的点,这个是可以递归维护出来的,那么我们只需要令 \(lx_x\) 表示在 \(x\) 为根的左子树内深度最小的点能走到的最远白点距离,类似地令 \(rx_x\) 表示在 \(x\) 为根的左子树内深度最大的点能走到的最远白点距离,那么我们 \(x\) 往上走到的最远白点和往下能走到的最远白点距离就可以用 \(lx, rx\) 来表示了,这样向上更新答案就只需要分类讨论即可,\(lx, rx\) 的更新也类似。注意最后一个细节,我们这里都是维护的链长,但 \(\rm LCT\) 只能维护点权,注意到我们这个做法并不需要 \(\rm Makeroot\) 那么我们直接将边权下方到儿子节点即可,这样一来 \(x\) 能到达虚儿子中的最长链就是虚儿子的 \(lx_x\) 于是第二个 \(set\) 我们直接用来维护虚儿子 \(lx_x\) 即可,细节有点多,具体转移看代码。

其他同类型题目:

山村游历(Wander)

首都

「Antileaf's Round」我们的 CPU 遭到攻击

维护树上染色联通块

这类问题的一般套路是将不同颜色分别开 \(\rm LCT\) 分开维护。

当然也有特例比如下面这题。

经典例题1:[SDOI2017]树点涂色

题意:给定一颗大小为 \(n\) 的有根树,最开始每个点的颜色不同,需要支持几种操作:将 \(x\) 到根路径上的点涂上一种没有出现过的颜色,查询 \(x, y\) 之间不同颜色的数量,查询以 \(x\) 为根的子树内的点到达根的路径上不同颜色的数量的最大值。

首先要注意到树上每一段颜色互不相同,如果没有操作 \(3\) 那么我们只需要写一个 \(\rm LCT\) 支持区间覆盖和查询颜色段即可,但加入操作 \(3\) 后我们不能继续这么做所以需要转变思路。想到这类问题的一般套路将不同颜色分别维护一颗 \(\rm LCT\) 但这样显然不现实,因为颜色太多了,因此这种办法也行不通。但是我们需要注意到问题当中的修改操作只需要修改 \(x\) 到根上的一条路径并涂上一种新的颜色,这是不是有点像 \(\rm LCT\) 中的 \(\rm Access\) 操作?进一步想 \(\rm Access\) 后 \(x\) 到根的路径放到了同一颗 \(\rm Splay\) 当中,这是不是恰好对应着染成了同一种颜色,那么我们就有了一个想法,在同一 \(\rm Splay\) 中维护同一种颜色,那么 \(\rm Access\) 就会将 \(\rm Splay\) 和在一起也就相当于染成同一种颜色,那么操作 \(2\) 就只需要查询 \(x\) 到根有多少棵 \(\rm Splay\) 即可。但这样还是不能直接做操作 \(3\),我们想想能不能直接维护每个点到根的颜色数量呢?事实上是可以的,每次我们在 \(\rm Access\) 断连虚实儿子合并 \(\rm Splay\) 的时候对于虚儿子中的所有节点相当于减少了到根的一种颜色,而对于其实儿子相当于增加了一种到根的颜色,那么我们就只需要写一颗线段树支持子树修改即可。最后查询我们只需要查询子树内的最大值,用线段树维护最大值即可。

经典例题2:QTREE6 - Query on a tree VI

题意:给定一颗大小为 \(n\) 的树,初始所有点为黑色,需要支持两种操作:给定 \(u\) 查询有多少个 \(v\) 满足 \(u, v\) 的路径上均为同种颜色,修改某个点的颜色(白变黑,黑变白)

首先按照该类型的套路我们分开维护两个 \(\rm LCT\),在白色 \(\rm LCT\) 上将树上边两端均为白点的边连上,黑色 \(\rm LCT\) 类似。于是每次查询我们只需要输出 \(x\) 所在连通块的大小即可,但是每次修改操作需要暴力枚举 \(x\) 周围的边暴力删边,这样菊花图就可以卡掉这个做法,于是我们需要考虑将这个做法优化。不难发现这个做法的问题在于 \(\rm LCT\) 只能支持删边操作而我们需要支持删点操作,那么我们如果能将删点转化为删边问题将得以解决。根据我们边权下方到儿子节点的套路,我们能否反其道而行之将节点颜色放到边上呢,事实上是可以的。具体来讲我们对于一个白点 \(x\) 我们将它和父亲的边在白色 \(\rm LCT\) 上连上,在黑色 \(\rm LCT\) 上断开,黑点相反。那么不难发现去掉当前联通块的根即最上面那个点后的连通块就和原来我们删点后的联通块一致了,因为最上面那个点与父亲没有边,也就意味这他不会属于这个联通块。那么对于每次查询,我们 \(\rm Access(x), Splay(x)\) 后查询右子树的大小加 \(1\) 即可。

其他同类型题目:

[ZJOI2012]网络

QTREE7 - Query on a tree VII

Jabby's shadows

至此 \(\rm LCT\) 的基本操作和经典例题已经记录完毕,剩下的就是 \(\rm LCT\) 的一些终极难题。

[Ynoi2017]由乃的OJ

共价大爷游长沙

[ZJOI2016]大森林

[ZJOI2018]历史

[THUWC 2017]在美妙的数学王国中畅游

LCT 入门的更多相关文章

  1. LCT入门总结

    原文链接https://www.cnblogs.com/zhouzhendong/p/LCT.html 为什么要写这个总结? 因为之前的总结出问题了…… 下载链接: LCT 入门总结 UPD(2019 ...

  2. 「专题总结」LCT入门

    上次xuefeng说我的专题总结(初探插头dp)太不适合入门了,所以这次丢一些题解包以外的东西. 关键是我自己也不会...急需梳理一下思路... (让我口胡数据结构???顺便推广一下全世界最短的lct ...

  3. LCT入门

    前言 \(LCT\),真的是一个无比神奇的数据结构. 它可以动态维护链信息.连通性.边权.子树信息等各种神奇的东西. 而且,它其实并不难理解. 就算理解不了,它简短的代码也很好背. \(LCT\)与实 ...

  4. BZOJ2843:极地旅行社(LCT入门题)

    不久之前,Mirko建立了一个旅行社,名叫“极地之梦”.这家旅行社在北极附近购买了N座冰岛,并且提供观光服 务.当地最受欢迎的当然是帝企鹅了,这些小家伙经常成群结队的游走在各个冰岛之间.Mirko的旅 ...

  5. BZOJ2049:Cave 洞穴勘测 (LCT入门)

    辉辉热衷于洞穴勘测.某天,他按照地图来到了一片被标记为JSZX的洞穴群地区.经过初步勘测,辉辉发现这片区域由n个洞穴(分别编号为1到n)以及若干通道组成,并且每条通道连接了恰好两个洞穴.假如两个洞穴可 ...

  6. 【BZOJ4025】二分图(LCT动态维护图连通性)

    点此看题面 大致题意: 给你一张图以及每条边的出现时间和消失时间,让你求每个时间段这张图是否是二分图. 二分图性质 二分图有一个比较简单的性质,即二分图中不存在奇环. 于是题目就变成了:让你求每个时间 ...

  7. 【转载】LCT题单

    本篇博客的题单转载自FlashHu大佬的博客:LCT总结--应用篇(附题单)(LCT). 关于\(LCT\)可以查看这篇博客:\(LCT\)入门. 这里面有些题解的链接是空链接,尚未补全. 维护链信息 ...

  8. 【SDOI2008】解题汇总

    好叭我真的是闲的了... /---------------------------------------------/ BZOJ-2037 [SDOI2008]Sue的小球 DP+相关费用提前计算 ...

  9. bzoj2049: [Sdoi2008]Cave 洞穴勘测

    lct入门题? 得换根了吧TAT 这大概不是很成熟的版本.. #include<iostream> #include<cstring> #include<cstdlib& ...

随机推荐

  1. HDU 6608:Fansblog(威尔逊定理)

    Fansblog Time Limit: 2000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) Total Subm ...

  2. Adversarially Robust Generalization Requires More Data

    目录 概 主要内容 高斯模型 upper bound lower bound 伯努利模型 upper bound lower bound Schmidt L, Santurkar S, Tsipras ...

  3. html5调用摄像头并拍照

    随着flash被禁用,flash上传附件的方式已成为过去,现在开始用html5上传了.本片文章就是介绍如何使用html5拍照,其实挺简单的原理: 调用摄像头采集视频流,利用canvas的特性生成bas ...

  4. [C++]C++ STL库函数大全

    #include <assert.h> //设定插入点 #include <ctype.h> //字符处理 #include <errno.h> //定义错误码 # ...

  5. <数据结构>拓扑排序

    有向无环图 有向无环图(Directed Acycilc Graph, DAG):从任意顶点出发都无法回到自身的有向图. 拓扑排序 定义 任一两个顶点u,v间,如果存在边u->v,则排序后u一定 ...

  6. Java面向对象笔记 • 【第5章 异常处理】

    全部章节   >>>> 本章目录 5.1 异常概述 5.1.1 程序中的异常 5.1.2 异常分类 5.1.3 实践练习 5.2 try-catch处理异常 5.2.2 使用f ...

  7. Python原生数据结构增强模块collections

    collections简介 python提供了4种基本的数据结构:list.tuple.dict.set.基本数据结构完全可以hold住所有的场景,但是在处理数据结构复杂的场景时,这4种数据结构有时会 ...

  8. Oracle打怪升级之路二【视图、序列、游标、索引、存储过程、触发器】

    前言 在之前 <Oracle打怪升级之路一>中我们主要介绍了Oracle的基础和Oracle常用查询及函数,这篇文章作为补充,主要介绍Oracle的对象,视图.序列.同义词.索引等,以及P ...

  9. APP自动化测试之手机滑屏

    相信大家在安装一个APP之后,进入之前会有几个页面组成的滑屏欢迎页面,要对这个APP进行自动化测试之前,就需要实现自动滑屏,怎么实现呢?请继续往下看 滑屏分 左滑和右滑,上滑.下滑 实现的原理(左滑) ...

  10. [ vue ] quasar框架踩坑:在vue文件外导入路由,执行router.push('/')没有效果

    问题描述: 1. 如图所示的项目结构目录, axios.js 文件负责拦截全局请求和回复,我在拦截回复的代码中写了:如果服务器回复了一个401错误,则执行Router.push('/'),但是该方法失 ...