全局平衡二叉树
注:博客1讲得比较详细,博客2的代码和博客1区别。
时间复杂度:(mathcal{O(nlogn)})
但是 常数比较大(但也没有LCT那么大)!虽然树链剖分的时间复杂度是 (mathcal{O(n logn^2)}),但是树链剖分有时会更快。复杂度允许下,能用树剖还是用树剖好。
作用:
全局平衡二叉树是静态树,即区别于LCT,建成后,树的形态不变。同树链剖分,可用于树的修改和查询。
构造:
跟树剖类似,求出树的重儿子(重链)。
对于每一根重链(一根,即 从链头到链尾),建一棵平衡二叉树,然后将链头节点的父亲用作二叉树的根的父亲。对于每一根轻链,直接保存其父亲为原始父亲。
在构建平衡二叉树时,以结点的轻儿子子树总和+1的大小作为权重,亦即,以 (siz[u]-siz[son[u]]) 作为节点 (u) 的权重。
构建时需保留在新树中:
- 所有节点的父亲: (tfa[u])
- 重节点的左右儿子:(ch[u][0]) ,(ch[u][1])
- 根据需要,保留节点深度:(dep[u])
- 根据需要,保留当前节点子树的信息:(val[u]/sum[u]/mi[u]...)
过程:
-
处理出 (lsiz) 和 (son)
int siz[maxn],lsiz[maxn],son[maxn]; void dfs(int u,int f) { siz[u]=1; for(int i=head[u];i;i=nxt[i]) if(to[i]!=f){ dfs(to[i],u); siz[u]+=siz[to[i]]; if(siz[to[i]]>siz[son[u]])son[u]=to[i]; } lsiz[u]=siz[u]-son[u]; }//处理出lsiz和son
-
建树
int tfa[maxn],ch[maxn][2],dep[maxn]; int si[maxn],vsi[maxn],tot;//si存重链节点序列 vsi是重链节点序列权值(即lsiz)前缀和 bool vis[maxn]; int update(int u){}//更新节点信息 比如 mi[u]=min(val[u],mi[ch[u][]]); int pushup(int u){ if(!u)return; if(getson(u)!=-1)update(u);// 节点u是其父亲的左儿子或右儿子 } int sbuild(int l,int r,int d)//先看build 再看sbuild { if(l>r)return 0; int mid=lower_bound(vsi+l,vsi+r+1,vsi[r]+vsi[l]>>1)-vsi; int u=si[mid];dep[u]=d; if(ch[u][0]=sbuild(1,mid-1,d+1))tfa[ch[u][0]]=u; if(ch[u][1]=sbuild(mid+1,r,d+1))tfa[ch[u][1]]=u; update(u); } int build(int u,int d) { int pos; for(pos=u,tot=0;pos;pot=son[pos]){ si[++tot]=pos;vsi[tot]=vsi[tot-1]+lsiz[pos];vis[pos]=1; }//当前点必是重链头节点 然后保存重链序列 int rt=sbuild(1,tot,d);//对当前重链建平衡树 for(pos=u;pos;pos=son[u]) for(int i=head[pos];i;i=nxt[i]) if(!vis[to[i]])//非重儿子 即 新的重链头 tfa[build(to[i],dep[pos]+1)]=pos;//当前节点作为 建成的子平衡树的根 的父亲 }
-
查询
找到的几个全局平衡树的博客都是luogu动态dp的这个例题,即 查询是对子树的查询,对子树的查询,在建树和更新过程中重要保存好子树信息,查询时直接输出即可。
而对于链的查询,比如查询链 ((u,v)) 的点权和,或者链 ((u,v)) 的点权最值,则需要 (log) 。
找不到相关博客,想到的方法或许不是最优。具体如下:
-
需要先用 (st) 表 预处理原树的 (lca) ,用来 (mathcal{O(1)}) 计算两点间距离。设 (pu=u,pv=v)
-
对于 (u,v) ,同树剖一样,每次处理都是处理 (dep) 较大的节点,然后计算信息后向上处理。
-
对于当前节点为 (u) 且 (u) 在链上,判断 (tfa[u]) 是否在 链 ((pu,pv)) 上,且设
[x=(ch[tfa[u]][1]==u)?1:(ch[tfa[u]][0]==u?0:-1) ]-
(tfa[u]) 在链上,则 (tfa[u]) 和 (u) 可能在同一棵或不同一棵平衡树上,即 (x) 是否等于 -1。
假如 (x==-1),则需计算 (ch[u][0]) 的子树信息。否则 计算 (ch[u][x)^(1]) 的子树信息。
再计算 (tfa[u]) 的节点信息
-
(tfa[u]) 不在链上,则 (tfa[u]) 和 (u) 必定在同一棵平衡二叉树上。只需计算 (ch[u][x]) 的子树信息。
-
-
令 (u=tfa[u]) 。
具体代码如下:
inline bool getson(int x){ return (ch[tfa[x]][1]==x)?1:((ch[tfa[x]][0]==x)?0:-1); } inline bool online(int u,int v,int po){ returndis(u,po)+dis(v,po)==dis(u,v); } //比如计算 链u v之间的最小点权 int cal(int u,int v) { int x,pu=u,pv=v;bool juu=1,juv=1,juf; int res=min(val[u],val[v]); while(u!=v) { if(dep[u]<dep[v])swap(u,v),swap(juu,juv); x=getson(u); juf=online(pu,pv,tfa[u]); if(!juf&&juu){ if(ch[u][x])res=min(res,mi[ch[u][x]]); }else if(juf&&juu){ if(x==-1)res=min(res,mi[ch[u][0]]); else res=min(res,mi[ch[u][x^1]]); res=min(res,val[tfa[u]]) } u=tfa[u];juu=juf; } return res; }
-
一道例题的代码(虽然因为常数过大而超时,树剖反而不超时
hdu6765(2020多校2C)
//#pragma GCC optimize(2)
#include<algorithm>
#include<cstdio>
#include<ctime>
#define min(a,b) (a)<(b)?(a):(b)
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int maxn=5e5+5;
char buf[1<<20],*P1=buf,*P2=buf;
//#define gc() (P1==P2&&(P2=(P1=buf)+fread(buf,1,1<<20,stdin),P1==P2)?EOF:*P1++)
#define gc() getchar()
#define TT template<class T>inline
TT void read(T&x){
x=0;register char c=gc();register bool f=0;
while(c<48||c>57){f^=c=='-',c=gc();}
while(47<c&&c<58)x=(x<<3)+(x<<1)+(c^48),c=gc();
if(f)x=-x;
}
int to[maxn<<1],nxt[maxn<<1];
int head[maxn],ecnt;
inline void addedge(int u,int v)
{
to[++ecnt]=v;nxt[ecnt]=head[u];
head[u]=ecnt;
}
int depth[maxn<<1],id[maxn],rid[maxn<<1],cnt,st[maxn<<1][25];
void dfs(int u,int f,int d)
{
id[u]=++cnt;rid[cnt]=u;depth[cnt]=d;
for(int i=head[u];i;i=nxt[i])
if(to[i]!=f){
dfs(to[i],u,d+1);
rid[++cnt]=u;depth[cnt]=d;
}
}
int lg[maxn<<1];
void init()
{
lg[0]=-1;
for(int i=1;i<=cnt;i++)lg[i]=lg[i>>1]+1;
for(int i=1;i<=cnt;i++)st[i][0]=i;
for(int j=1;(1<<j)<=cnt;j++)
for(int i=1;i+(1<<j)-1<=cnt;i++)
st[i][j]=depth[st[i][j-1]]<depth[st[i+(1<<j-1)][j-1]]?
st[i][j-1]:
st[i+(1<<j-1)][j-1];
}
inline int lca(int u,int v)
{
if(id[u]>id[v])swap(u,v);
int s=id[u],t=id[v],len=lg[t-s+1];
return depth[st[s][len]]<depth[st[t-(1<<len)+1][len]]?rid[st[s][len]]:rid[st[t-(1<<len)+1][len]];
}
inline int dis(int u,int v){
return depth[id[u]]+depth[id[v]]-(depth[id[lca(u,v)]]<<1);
}
//gbbt
int siz[maxn],lsiz[maxn],son[maxn];
void dfs1(int u,int f)//处理son和lsiz 的信息
{
siz[u]=1;
for(int i=head[u];i;i=nxt[i])
if(to[i]!=f){
dfs1(to[i],u);
siz[u]+=siz[to[i]];
if(siz[to[i]]>siz[son[u]])son[u]=to[i];
}
lsiz[u]=siz[u]-siz[son[u]];
}
int col[maxn];
int tfa[maxn],ch[maxn][2];
const int N=30;
unsigned int mi[maxn][N],val[maxn][N];
inline void update(int u){
for(int i=0;i<N;i++){
mi[u][i]=val[col[u]][i];
if(ch[u][0])mi[u][i]=min(mi[u][i],mi[ch[u][0]][i]);
if(ch[u][1])mi[u][i]=min(mi[u][i],mi[ch[u][1]][i]);
}
}
inline int getson(int x){return (ch[tfa[x]][1]==x)?1:((ch[tfa[x]][0]==x)?0:-1);}
void pushup(int u){
update(u);
if(getson(u)!=-1)pushup(tfa[u]);
}
int si[maxn],tot;ll vsi[maxn];
int dep[maxn];bool vis[maxn];
int sbuild(int l,int r,int d)
{
if(l>r)return 0;
int mid=lower_bound(vsi+l,vsi+r+1,vsi[r]+vsi[l-1]>>1)-vsi;
int u=si[mid];dep[u]=d;
if(ch[u][0]=sbuild(l,mid-1,d+1))tfa[ch[u][0]]=u;
if(ch[u][1]=sbuild(mid+1,r,d+1))tfa[ch[u][1]]=u;
update(u);
return u;
}
int build(int u,int d)
{
int pos;
for(pos=u,tot=0;pos;pos=son[pos]){
si[++tot]=pos;vsi[tot]=vsi[tot-1]+lsiz[pos];
vis[pos]=1;
}
int rt=sbuild(1,tot,d);
for(pos=u;pos;pos=son[pos])
for(int i=head[pos];i;i=nxt[i])
if(!vis[to[i]])
tfa[build(to[i],dep[pos]+1)]=pos;
return rt;
}
inline bool online(int u,int v,int po){
return dis(u,po)+dis(v,po)==dis(u,v);
}
unsigned int tmp[N];
ll cal(int u,int v)
{
int x,pu=u,pv=v;bool juu=1,juv=1,fju;
for(int i=0;i<N;i++)tmp[i]=min(val[col[u]][i],val[col[v]][i]);
while(u!=v)
{
if(dep[u]<dep[v])swap(u,v),swap(juu,juv);//dep u > dep v
x=getson(u);
fju=online(pu,pv,tfa[u]);
if(!fju){
if(juu&&ch[u][x]){
for(int i=0;i<N;i++)
tmp[i]=min(tmp[i],mi[ch[u][x]][i]);
}
}else{
if(juu&&x==-1&&ch[u][0]){
for(int i=0;i<N;i++)
tmp[i]=min(tmp[i],mi[ch[u][0]][i]);
}
else if(juu&&x!=-1&&ch[u][x^1]){
x^=1;
for(int i=0;i<N;i++)
tmp[i]=min(tmp[i],mi[ch[u][x]][i]);
}
for(int i=0;i<N;i++)
tmp[i]=min(tmp[i],val[col[tfa[u]]][i]);
}
juu=fju;u=tfa[u];
}
ll sum=0;
for(int i=0;i<N;i++)sum+=tmp[i];
return sum;
}
mt19937 mt(time(0));
int main()
{
// freopen("C:\Users\admin\Desktop\tmp\1003.in","r",stdin);
// freopen("C:\Users\admin\Desktop\tmp\1003out.txt","w",stdout);
for(int i=0;i<maxn;i++)
for(int j=0;j<N;j++)val[i][j]=mt();
int T;
read(T);
while(T--)
{
int n,m,u,v,rt,lans=0;
read(n);read(m);
for(int i=tot=ecnt=cnt=0;i<=n;i++)
vis[i]=vsi[i]=head[i]=dep[i]=tfa[i]=son[i]=0;
for(int i=1;i<=n;i++)read(col[i]);
for(int i=1;i<n;i++)
{
read(u);read(v);
addedge(u,v);addedge(v,u);
}
dfs(1,0,0);init();dfs1(1,0);build(1,1);
int op,x,y;ll sum1,sum2;
while(m--)
{
read(op);read(x);read(y);
if(op==1){
x^=lans;y^=lans;
col[x]=y;pushup(x);
}else if(op==2){
read(u);read(v);
x^=lans;y^=lans;u^=lans;v^=lans;
sum1=cal(x,y);sum2=cal(u,v);
if(sum1<sum2)puts("Yes"),lans++;
else puts("No");
}
}
}
}