题目描述


n,m≤105n,m\le10^5n,m≤105,点的颜色范围为 [1,30][1,30][1,30]

题目分析

LCT可以维护黑白两色,黑点向父亲连边,实际连通块去掉根。但是整个连通块同时变色就不好整了。

用ETT(就是dfs序splay)维护边连通块,这样改变颜色的时候只会改变这条边与上下的连通性。用ETT是因为可以支持快速换子树,因为同一子树关键字是连续的。

初始时看做同一种颜色,按dfs序建树,splay点标对应原树点标。

一个边连通块为一棵以dfs序为关键字的 splay。

上图中同一种颜色的箭头指向的节点在同一棵 splay 中。

(link cut 之后实际上不同子树之间不一定是按原树dfs序排的,但是同一子树的关键字一定是连续的)

描述一下怎么操作(盯std盯了2h…):

查询 xxx

x=getroot(x)x=getroot(x)x=getroot(x)

getroot(x)getroot(x)getroot(x) 是找到 xxx 所在边连通块的顶端,先找到 xxx splay 中关键字最小的节点(一定是同层的某个同颜色顶端),得到 xxx 与它的顶端的深度差,然后倍增往上跳。

上图中 getroot(3)=2getroot(3)=2getroot(3)=2

rest=split(x)rest=split(x)rest=split(x)

split(x)split(x)split(x) 是将 xxx 原树子树的边连通块与它所在的大连通块分离,返回值为分离之后的大连通块的 splay 的根。为了找到 xxx 原树子树后面的关键字最小的点,平衡树中需要存 ST,EDST,EDST,ED 表示子树中 dfsdfsdfs 序的最小值和最大值。

上图中 split(2)=5split(2)=5split(2)=5

printf(col[x],siz[x],mx[x]−dep[x]+1)printf(col[x],siz[x],mx[x]-dep[x]+1)printf(col[x],siz[x],mx[x]−dep[x]+1)

mx[x]mx[x]mx[x] 是 splay 维护的子树中的最大深度。

if(rest≠−1)merge0(rest,x)if(rest\neq-1)~merge_0(rest,x)if(rest​=−1) merge0​(rest,x)

merge0(x,y)merge_0(x,y)merge0​(x,y) 是将同层的两个同色边连通块合并。

改变 xxx 的颜色为 ccc

首先 splay(x,0)splay(x,0)splay(x,0),因为要把上面的标记传下来,才知道 xxx 的 colorcolorcolor

if(col[x]==c)return;if(col[x]==c)~return;if(col[x]==c) return;

先分离出 xxx,rest=split(x)rest=split(x)rest=split(x)

if(siz[x]>1)if(siz[x]>1)if(siz[x]>1) 断掉 xxx 的后继与 xxx 的连边(xxx 不再与它

们同色),并更新 son[x][col[x]]son[x][col[x]]son[x][col[x]] 为它的后继。

son[x][c]son[x][c]son[x][c] 记录树上 xxx 这个点接的颜色为 ccc 的边连通块中 xxx 的某个儿子。为了合并连通块时快速查找。还需要维护 s[x]s[x]s[x] ,二进制第 ccc 位表示 son[x][c]son[x][c]son[x][c] 是否为 1,以及 S[x]S[x]S[x] 表示 splay 子树中 sss 的或。son[x][c]son[x][c]son[x][c] 只维护虚儿子,即不与 xxx 颜色相同的 ccc 处有值,因为同色的没有用,而且整个连通块变色的时候还要额外维护清零标记。

然后考虑合并 xxx 与下面的连通块:if(son[x][c])merge1(x,son[x][c])if(son[x][c])~merge_1(x,son[x][c])if(son[x][c]) merge1​(x,son[x][c])

merge1(x,y)merge_1(x,y)merge1​(x,y) 是将不同层(父子关系)的边连通块合并。

然后将 son[x][c]son[x][c]son[x][c] 清零,s[x]s[x]s[x] 去掉第 ccc 位。(注意这些修改都要在 xxx 为根的情况下做)

然后是 xxx 与上面的连通块:f=fa[x]f=fa[x]f=fa[x]

splay(f,0)splay(f,0)splay(f,0)

if(col[f]≠col[x])if(col[f]\neq col[x])if(col[f]​=col[x]) 更改 son[f][col[x]]son[f][col[x]]son[f][col[x]] 为 restrestrest 。分 restrestrest 是否为 −1-1−1 讨论。

if(col[f]≠c)if(col[f]\neq c)if(col[f]​=c) 将 xxx 与 son[f][c]son[f][c]son[f][c] 合并。分 son[f][c]son[f][c]son[f][c] 的有无讨论。

elseelseelse 合并 f,xf,xf,x

改变 xxx 同色连通块的颜色为 ccc

x=getroot(x)x=getroot(x)x=getroot(x),如果颜色不用改直接 return。

rest=split(x)rest=split(x)rest=split(x)

给 xxx 打上颜色 ccc 的标记。

travel(x,c)travel(x,c)travel(x,c):找到 xxx 边连通块下方颜色为 ccc 的点与其对应的父亲,根据 S[x]S[x]S[x] 确定是否进子树,根据 son[x][c]son[x][c]son[x][c] 确定。

找完之后依次将找到的连通块与 xxx 合并。合并的总次数是 O(n+m)O(n+m)O(n+m) 的。

与 xxx 父亲的连通块的修改情况与单点修改一模一样。


呼~~~(描述完操作过了40min…)

尽管看起来很繁琐,实际上实现还要更繁琐,但是看了2h代码之后思路还是比较明了的。

Code:

#include<bits/stdc++.h>
#define maxn 100005
using namespace std;
int n,m,F[17][maxn],dep[maxn],st[maxn],ed[maxn],ln[maxn],tim;
int col[maxn],son[maxn][30];
vector<pair<int,int>>vec;
namespace ETT{int fa[maxn],ch[maxn][2],mx[maxn],ST[maxn],ED[maxn];int S[maxn],s[maxn],tag[maxn],siz[maxn];bool isc(int x){return ch[fa[x]][1]==x;}void upd(int x){siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1;mx[x]=max(dep[x],max(mx[ch[x][0]],mx[ch[x][1]]));ST[x]=min(st[x],min(ST[ch[x][0]],ST[ch[x][1]]));ED[x]=max(ed[x],max(ED[ch[x][0]],ED[ch[x][1]]));S[x]=s[x]|S[ch[x][0]]|S[ch[x][1]];}void paint(int x,int c){if(x) col[x]=c,tag[x]=c;}void pd(int x){if(~tag[x]) paint(ch[x][0],tag[x]),paint(ch[x][1],tag[x]),tag[x]=-1;}void pdpath(int x){if(fa[x]) pdpath(fa[x]); pd(x);}void rot(int x){int y=fa[x],z=fa[y],c=isc(x);if(z) ch[z][isc(y)]=x;fa[ch[y][c]=ch[x][!c]]=y, fa[ch[x][!c]=y]=x, fa[x]=z;upd(y);}void splay(int x,int goal=0){for(pdpath(x);fa[x]!=goal;rot(x))if(fa[fa[x]]!=goal) rot(isc(x)==isc(fa[x])?fa[x]:x);upd(x);}int findL(int x){while(ch[x][0]) x=ch[x][0]; return x;}int findR(int x){while(ch[x][1]) x=ch[x][1]; return x;}int Next(int x,int l,int r){if(!x) return 0;if(ST[ch[x][0]]<l||ED[ch[x][0]]>r) return Next(ch[x][0],l,r);if(st[x]<l||ed[x]>r) return x;return Next(ch[x][1],l,r);}int split(int x){int l=findR(ch[x][0]),r=Next(ch[x][1],st[x],ed[x]),rt;if(!l&&!r) rt=0;else if(!r) splay(l),fa[ch[l][1]]=0,ch[l][1]=0,upd(rt=l);else if(!l) splay(r),fa[ch[r][0]]=0,ch[r][0]=0,upd(rt=r);else splay(l),splay(r,l),fa[ch[r][0]]=0,ch[r][0]=0,upd(r),upd(rt=l);return splay(x),rt;}void merge0(int x,int y){splay(x),splay(x=findL(x)),splay(y),fa[ch[x][0]=y]=x,upd(x);}void merge1(int f,int x){splay(f),splay(x);if(ch[f][1]) splay(findL(ch[f][1]),f),fa[ch[ch[f][1]][0]=x]=ch[f][1],upd(ch[f][1]),upd(f);else fa[ch[f][1]=x]=f,upd(f);}void travel(int x,int c){if(son[x][c]) vec.push_back(make_pair(x,son[x][c])),son[x][c]=0,s[x]^=1<<c;if(S[ch[x][0]]>>c&1) travel(ch[x][0],c);if(S[ch[x][1]]>>c&1) travel(ch[x][1],c);upd(x);}int build(int l,int r,int ff){if(l>r) return 0;int mid=(l+r)>>1,i=ln[mid];fa[i]=ff,tag[i]=-1;if(l==r) return upd(i),i;ch[i][0]=build(l,mid-1,i),ch[i][1]=build(mid+1,r,i);return upd(i),i;}
}
using namespace ETT;
int fir[maxn],nxt[maxn];
void line(int x,int y){nxt[y]=fir[x],fir[x]=y;}
void dfs(int u){ln[st[u]=++tim]=u;for(int v=fir[u];v;v=nxt[v]) dep[v]=dep[u]+1,dfs(v);ed[u]=tim;
}
int getroot(int x){splay(x); int r=findL(x);for(int i=0,d=dep[x]-dep[r];d;d>>=1,i++) if(d&1) x=F[i][x];return splay(x),x;
}
void solve1(int x,int c){splay(x,0);int y=col[x]; if(y==c) return;int rest=split(x);if(siz[x]>1){//断掉 $x$ 的后继与 $x$ 的连边son[x][y]=findL(ch[x][1]),s[x]^=1<<y;fa[ch[x][1]]=0,ch[x][1]=0;upd(x),splay(son[x][y]);}col[x]=c;if(son[x][c]) merge1(x,son[x][c]),splay(x),son[x][c]=0,s[x]^=1<<c,upd(x);if(x!=1){int f=F[0][x];splay(f);if(col[f]!=y){if(!rest) son[f][y]=0,s[f]^=1<<y,upd(f);else splay(son[f][y]=findL(rest));}if(col[f]!=c){if(son[f][c]) merge0(x,son[f][c]);else son[f][c]=x,s[f]^=1<<c,upd(f);}else merge1(f,x);}
}
void solve2(int x,int c){x=getroot(x);int y=col[x]; if(y==c) return;int rest=split(x);paint(x,c);vec.clear(),travel(x,c);
//  for(int i=0;i<vec.size();i++) cout<<vec[i].first<<' '<<vec[i].second<<endl;for(int i=0;i<vec.size();i++) merge1(vec[i].first,vec[i].second);if(x!=1){int f=F[0][x];splay(f);if(col[f]!=y){//certainly.if(!rest) son[f][y]=0,s[f]^=1<<y,upd(f);else splay(son[f][y]=findL(rest));}if(col[f]!=c){if(son[f][c]) merge0(x,son[f][c]);else son[f][c]=x,s[f]^=1<<c,upd(f);}else merge1(f,x);}
}
void solve3(int x){x=getroot(x);int rest=split(x);printf("%d %d %d\n",col[x]+1,siz[x],mx[x]-dep[x]+1);if(rest) col[F[0][x]]==col[x]?merge1(F[0][x],x):merge0(rest,x);
}
int main()
{freopen("tree.in","r",stdin);freopen("tree.out","w",stdout);scanf("%d",&n);for(int i=2,x;i<=n;i++) scanf("%d",&x),line(x,i),F[0][i]=x;for(int j=1;j<=16;j++) for(int i=1;i<=n;i++) F[j][i]=F[j-1][F[j-1][i]];dfs(1);ST[0]=1e9,col[0]=30,build(1,tim,0);for(int i=1,x;i<=n;i++) scanf("%d",&x),solve1(i,x-1);scanf("%d",&m);for(int op,x,y;m--;){scanf("%d%d",&op,&x);if(op==3) solve3(x);else scanf("%d",&y),y--,op==1?solve1(x,y):solve2(x,y);}
}

20200726 T3 树高【ETT(dfs序splay)维护同色边连通块】相关推荐

  1. BZOJ 2434: [Noi2011]阿狸的打字机 [AC自动机 Fail树 树状数组 DFS序]

    2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec  Memory Limit: 256 MB Submit: 2545  Solved: 1419 [Submit][S ...

  2. 【XSY2667】摧毁图状树 贪心 堆 DFS序 线段树

    题目大意 给你一棵有根树,有\(n\)个点.还有一个参数\(k\).你每次要删除一条长度为\(k\)(\(k\)个点)的祖先-后代链,问你最少几次删完.现在有\(q\)个询问,每次给你一个\(k\), ...

  3. [BZOJ 2434][Noi2011]阿狸的打字机(AC自动机+树状数组+dfs序)

    Description 打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母.经阿狸研究发现,这个打字机是这样工作的: ·输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母 ...

  4. HDU - 5877 Weak Pair(离散化+树状数组+dfs序)

    题目链接:点击查看 题目大意:给定一个n个节点的树,每个节点都有权值,现在定义weak pair(u,v)需要满足的两个条件: u是v的祖先: ; 问给定的树中有多少个weak pair: 题目分析: ...

  5. 【BZOJ3551】Peaks加强版,主席树+kruskal重构+dfs序+倍增思想

    传送门 写在前面:一道调了不止一上午的题目 思路:我们先来看一下神奇的kruskal重构树 图中红色的是原图的边权,黑色的是原图上的点 这样生成的树有一些十分优美的性质: 1.二叉树(好吧这题意义不大 ...

  6. 【BZOJ3653】谈笑风生 离线+树状数组+DFS序

    [BZOJ3653]谈笑风生 Description 设T 为一棵有根树,我们做如下的定义: ? 设a和b为T 中的两个不同节点.如果a是b的祖先,那么称"a比b不知道高明到哪里去了&quo ...

  7. Apple Tree(树状数组+dfs序+邻接表数组(链式前向星) )

    链接:http://poj.org/problem?id=3321 Description There is an apple tree outside of kaka's house. Every ...

  8. 【2021牛客暑期多校训练营7】xay loves trees(dfs序,维护根出发的链)

    F xay loves trees 题意: 给出两棵树,由这两棵树根据规则可以生成一个图,规则如下:如果u , v在第一棵树中满足其中一个点是另一个点祖先且最终所有所选的点都互相联通,在第二棵树中满足 ...

  9. CF1528C dfs序+set维护

    传送门 文章目录 题意: 思路: 题意: 给你两棵有nnn个节点的树,我门记第一棵为aaa,第二棵为bbb,现在你有一个nnn个点都孤立的点集,两个点u,vu,vu,v可以连边当且仅当这两个点在aaa ...

  10. CF-547E(Mike and Friends)后缀数组+线段树 AC自动机+DFS序+树状数组

    题目链接 题意 NNN个串,每次询问区间[L,R][L,R][L,R]中有多少子串SiS_iSi​ 思路 把NNN个串合成一个长字符串,对这个长字符串求后缀数组,包含SiS_iSi​的子串的heigh ...

最新文章

  1. oracle for 记录数,可视化工具dbForge Documenter for Oracle全新上线!让您轻松记录Oracle数据库...
  2. MySQL 5.1中IN查询不要用到NULL条件
  3. 网页去重||SimHash(高效的文本相似度去重算法)——适合大批量文档的相似度计算
  4. Memcached 在linux上安装笔记
  5. 如何使用1Password,Authy和Privacy.com外包您的在线安全性
  6. 【机器视觉学习笔记】OpenCV C++ 调用笔记本摄像头
  7. 云原生时代来袭 下一代云数据库技术将走向何方?
  8. 分布式计算 MapReduce与yarn工作机制
  9. stack 和 heap区别
  10. (原)Lazarus 异构平台下多层架构思路、DataSet转换核心代码
  11. Sublime Text 插件 【转】
  12. 线性表表长是否要算入头结点
  13. 如何将每日新闻添加到自己博客中,发送到微信群中
  14. Silvaco TCAD仿真9——半导体PN结仿真
  15. STM32MP157A-DK1 烧写系统
  16. linux解压lzma,如何获得LZMA2文件的解压缩大小(.xz/liblzma)
  17. 说话技巧 为人处事
  18. PHP 十六大魔术方法学习
  19. (一)1.考研数一高数 之 函数及其性质
  20. 【网络编程】Linux tcpdump命令详解---编辑中

热门文章

  1. 手机CPU天梯图2018年5月最新版
  2. XCTF-MFW Git泄露,命令执行漏洞 详解
  3. 小蜜蜂无纸化考试系统 官网
  4. chrome边解析Html边显示,谷歌浏览器插件侧边翻译Edge Translate
  5. uniapp动态图片加载不出来
  6. 【Excle数据透视表】如何移动数据透视表的位置
  7. C++ deque类使用详解
  8. 【论文笔记】2022-CVPR-深度估计
  9. 几款笔记软件的优缺点
  10. 计算机的开机键在哪里设置,笔记本电脑怎么开机 笔记本电脑开机键在哪