一般提到动态树,我们会不约而同的想到 LCT,这算是比较通用,实用,能力较为广泛的一种写法了。当然,掌握 LCT 就需要熟悉掌握 Splay 和各种操作和知识。ETT(中文常用称呼:欧拉游览树)是一种及其睿智且暴力,可以用暴力数据结构维护的一种除了能胜任普通动态树的 Link & Cut 操作还可以支持换子树操作(此操作 LCT 无法完成)的动态树。

大家对这括号序很熟悉吧,如:

其括号序为:1 2 5 5 6 6 2 3 3 4 7 8 8 7 4 1。

括号序其实是一个父亲包含儿子的一种树的顺序。

然后我们看一下,如果把 4 的子树移给 3 会怎样?如图:

原图括号序:1 2 5 5 6 6 2 3 3 4 7 8 8 7 4 1

后者括号序:1 2 5 5 6 6 2 3 7 8 8 7 3 4 4 1

可以发现,7 8 8 7 平移到了 3 的后面,而 4 合拢。这就是所谓换子树操作(同样可以用于 Link & Cut 操作)。现在只需要一个数据结构可以做到区间平移且维护一些值,众大佬肯定会说用 Splay,其确实效率很高,不过这里用块状链表维护会简单很多,对于一些数据低于 的题目都可以码得很快。

那怎么维护点到根的信息呢?

其实仔细想想,DFS 序也可以达到平移的效果,那么为什么需要括号序?其实,假如你要查询图中 1 到 8 的和,那么你从括号序中 1 到 8(第一个出现的)中出现两次的数的贡献抹去。如果维护的是 xor,那么直接 xor 两次即可。如果维护的是 sum,那么第一个出现的数字的贡献为正,第二个为负,然后用块状链表维护区间和即可。

用块状链表后除了单点修改是 \(O(1)\) 外其他都是 \(O(\sqrt n)\) 的。

ETT 不支持换根操作。对于链(区间)修改,分为两种情况,一是贡献相同(如 xor)是可以的,二是贡献不同(如 sum)是不行的。现在的主流做法毕竟是 LCT,所以这些操作比较多,在避开这种操作的情况下运用这种做法还是不错的。

注:标准的 ETT(用欧拉回路而不是 DFS 括号序实现)是支持换根操作的,但是实现较为复杂。

#3786. 星系探索

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n,m;
int ver[200005],ne[200005],head[100005],cnt;
inline void link(int x,int y){
ver[++cnt]=y;
ne[cnt]=head[x];
head[x]=cnt;
}
int w[100005];
vector<int> vec;
int son[2][200005],val[200005],fa[200005],rt,siz[200005],tot[200005];
long long sum[200005],tag[200005];
inline void pushup(int x){
sum[x]=sum[son[0][x]]+val[x]+sum[son[1][x]];
tot[x]=tot[son[0][x]]+siz[x]+tot[son[1][x]];
}
inline void push(int x){
if(!tag[x])return ;
if(son[0][x]){
sum[son[0][x]]+=tag[x]*tot[son[0][x]];
val[son[0][x]]+=siz[son[0][x]]*tag[x];tag[son[0][x]]+=tag[x];
}
if(son[1][x]){
sum[son[1][x]]+=tag[x]*tot[son[1][x]];
val[son[1][x]]+=siz[son[1][x]]*tag[x];tag[son[1][x]]+=tag[x];
}tag[x]=0;
}
inline void rotate(int x,int &k){
int y=fa[x],z=fa[y];
if(y==k)k=x;else son[son[1][z]==y][z]=x;
bool is=(son[1][y]==x);
son[is][y]=son[!is][x];fa[son[!is][x]]=y;
son[!is][x]=y;fa[y]=x;fa[x]=z;pushup(x);pushup(y);
}
int stk[200005],top;
inline void splay(int x,int &k){
stk[++top]=x;
for(int i=x;i!=k;i=fa[i])stk[++top]=fa[i];
while(top)push(stk[top--]);
while(x!=k){
int y=fa[x],z=fa[y];
if(y!=k){
if((son[1][y]==x)^(son[1][y]==z))rotate(x,k);
else rotate(y,k);
}rotate(x,k);
}
}
int build(int l=0,int r=vec.size()-1){
if(l>r)return 0;
int mid=(l+r)>>1,i=vec[mid];
son[0][i]=build(l,mid-1);fa[son[0][i]]=i;
son[1][i]=build(mid+1,r);fa[son[1][i]]=i;pushup(i);
return i;
}
void dfs(int x,int fi){
tot[x]=siz[x]=1;
sum[x]=val[x]=w[x];vec.push_back(x);
for(int i=head[x];i;i=ne[i]){
int u=ver[i];
if(u==fi)continue;
dfs(u,x);
}
tot[x+n]=siz[x+n]=-1;
sum[x+n]=val[x+n]=-w[x];vec.push_back(x+n);
}
inline int pre(int x){
splay(x,rt);x=son[0][x];
while(son[1][x])x=son[1][x];
return x;
}
inline int nxt(int x){
splay(x,rt);x=son[1][x];
while(son[0][x])x=son[0][x];
return x;
}
void put(int x){
if(son[0][x])put(son[0][x]);
if(x<=2*n)printf("%d ",x<=n?x:-x+n);
if(son[1][x])put(son[1][x]);
}
inline int& split(int l,int r){
int x=pre(l),y=nxt(r);
splay(x,rt);splay(y,son[1][x]);
return son[0][y];
}
inline void update(int x,int v){
int y=split(x,x+n);
val[y]+=siz[y]*v;sum[y]+=tot[y]*v;
tag[y]+=v;pushup(fa[y]);push(fa[fa[y]]);
}
inline void move(int x,int y){
int &z=split(x,x+n),k=z;
z=0;pushup(fa[k]);pushup(fa[fa[k]]);
int t=split(y,y);son[1][t]=k;fa[k]=t;
pushup(t);pushup(fa[t]);pushup(fa[fa[t]]);
}
inline long long query(int x){
int y=nxt(x);
splay(y,rt);return sum[son[0][y]];
}
char op[15];
int main(){
scanf("%d",&n);
for(int i=2;i<=n;i++){
int x;scanf("%d",&x);
link(x,i);link(i,x);
}
for(int i=1;i<=n;i++)scanf("%d",&w[i]);
vec.push_back(2*n+1);dfs(1,1);
vec.push_back(2*n+2);rt=build();
scanf("%d",&m);
while(m--){
scanf("%s",op);
if(op[0]=='Q'){
int x;scanf("%d",&x);
printf("%lld\n",query(x));
}
else if(op[0]=='C'){
int x,y;scanf("%d%d",&x,&y);
move(x,y);
}
else if(op[0]=='F'){
int x,y;scanf("%d%d",&x,&y);
update(x,y);
}
} return 0;
}

[BJOI2014]大融合

点击查看代码

动态树 — Euler_Tour_Tree的更多相关文章

  1. 如何利用FineReport制作动态树报表

    在对数据字段进行分类管理时,利用动态树折叠数据是一个很好的方法,也就是点击数据前面的加号才展开对应下面的数据,如下图.那这样的效果在制作报表时该如何实现呢? 下面以报表工具FineReport为例介绍 ...

  2. 动态树之LCT(link-cut tree)讲解

    动态树是一类要求维护森林的连通性的题的总称,这类问题要求维护某个点到根的某些数据,支持树的切分,合并,以及对子树的某些操作.其中解决这一问题的某些简化版(不包括对子树的操作)的基础数据结构就是LCT( ...

  3. 【BZOJ-3589】动态树 树链剖分 + 线段树 + 线段覆盖(特殊的技巧)

    3589: 动态树 Time Limit: 30 Sec  Memory Limit: 1024 MBSubmit: 405  Solved: 137[Submit][Status][Discuss] ...

  4. BZOJ-2049 Cave洞穴勘测 动态树Link-Cut-Tree (并查集骗分TAT)

    2049: [Sdoi2008]Cave 洞穴勘测 Time Limit: 10 Sec Memory Limit: 259 MB Submit: 5833 Solved: 2666 [Submit] ...

  5. 学习笔记-动态树Link-Cut-Tree

    --少年你有梦想吗? --少年你听说过安利吗? 安利一个集训队讲解:http://wenku.baidu.com/view/75906f160b4e767f5acfcedb 关于动态树问题,有多种方法 ...

  6. BZOJ 3589 动态树(子树操作,链查询)

    题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=3589 题意:给出一棵有根树,两种操作:(1)以u为根的子树所有节点权值加上一个数字 ...

  7. Tsinsen A1517. 动态树 树链剖分,线段树,子树操作

    题目 : http://www.tsinsen.com/A1517 A1517. 动态树 时间限制:3.0s   内存限制:1.0GB    总提交次数:227   AC次数:67   平均分:49. ...

  8. 动态树 Link-Cut Trees

    动态树 动态树问题, 即要求我们维护一个由若干棵子结点无序的有根树组成的森林. 要求这个数据结构支持对树的分割.合并,对某个点到它的根的路径的某些操作,以及对某个点的子树进行的某些操作. 在这里我们考 ...

  9. bzoj 2594: [Wc2006]水管局长数据加强版 动态树

    2594: [Wc2006]水管局长数据加强版 Time Limit: 25 Sec  Memory Limit: 128 MBSubmit: 934  Solved: 291[Submit][Sta ...

随机推荐

  1. 2021.08.09 P7238 迷失森林(树的直径)

    2021.08.09 P7238 迷失森林(树的直径) P7238 「DCOI」迷失森林 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 重点: 1.树的直径两种求法:两次dfs.树 ...

  2. B3log开源博客compose搭建

    B3log开源博客搭建 docker 安装 yum install docker-ce-17.12.1.ce docker-compose 安装 curl -L https://github.com/ ...

  3. 6.1 SHELL脚本

    6.1 SHELL脚本元素 第一行的脚本声明(#!)用来告诉系统使用哪种Shell解释器来执行该脚本: 第二行的注释信息(#)是对脚本功能和某些命令的介绍信息,使得自己或他人在日后看到这个脚本内容时, ...

  4. 手写useState与useEffect

    手写useState与useEffect useState与useEffect是驱动React hooks运行的基础,useState用于管理状态,useEffect用以处理副作用,通过手写简单的us ...

  5. 使用fastai训练的一个性别识别模型

    在学习了python中的一些机器学习的相关模块后,再一次开始了深度学习之旅.不过与上次的TensorFlow框架不同,这一次接触的是fast.ai这样一个东西.这个框架还不稳定,网上也没有相关的中文文 ...

  6. WPF 制作雷达扫描图

    实现一个雷达扫描图. 源代码在TK_King/雷达 (gitee.com),自行下载就好了 制作思路 绘制圆形(或者称之轮) 绘制分割线 绘制扫描范围 添加扫描点 具体实现 首先我们使用自定义的控件. ...

  7. sklearn机器学习实战-简单线性回归

    记录下学习使用sklearn,将使用sklearn实现机器学习大部分内容 基于scikit-learn机器学习(第2版)这本书,和scikit-learn中文社区 简单线性回归 首先,最简单的线性回归 ...

  8. JS运算符,流程控制,函数,内置对象,BOM与DOM

    运算符 1.算数运算符 运算符 描述 + 加 - 减 * 乘 / 除 % 取余(保留整数) ++ 递加 - - 递减 ** 幂 var x=10; var res1=x++; '先赋值后自增1' 10 ...

  9. 日期和时间API - 读《Java 8实战》

    日期与时间 LocalDate 创建一个LocalDate对象并读取其值 // 根据年月日创建日期 LocalDate date1 = LocalDate.of(2014, 3, 18); // 读取 ...

  10. 122_Power Pivot&Power BI不连续日期的日环比

    博客:www.jiaopengzi.com 焦棚子的文章目录 请点击下载附件 一.背景 这两天有朋友在交流,dax中使用时间智能函数写日环比,一个 dateadd 就可以了.但是有些业务不是每天都连续 ...