重链剖分学习笔记
前置:线段树(或对一棵树树剖之后的维护用到的其他数据结构)、dfs 序。
定义
- 重儿子:子结点中子树大小最大的节点。
- 轻儿子:除重儿子外的其他所有子节点。
- 重边:非叶子节点到它的重儿子的边。
- 轻边:非叶子节点到它的轻儿子的边。
- 重链:若干条首尾相连接的重边构成的链。
- 链首:每条重链中深度最小的点。
实现
是将整棵树分为若干个重链的过程。两个 dfs 求出 dfs 序、子树大小、重儿子和链首即可。
因为需要实现某一条重链的操作,这个地方的 dfs 序需要先遍历重儿子,这样就可以确保一条重链的编号是连续的。
void dfs1(int u,int fa)
{
siz[u]=1;
for(auto v:g[u])
{
if(v==fa) continue;
dep[v]=dep[u]+1;
da[v]=u;
dfs1(v,u);
siz[u]+=siz[v];
if(siz[v]>siz[son[u]]) son[u]=v;
}
}
void dfs2(int u,int fa)
{
dfn[u]=++num;
if(son[u]) top[son[u]]=top[u],dfs2(son[u],u);
for(auto v:g[u])
{
if(v==fa||v==son[u]) continue;
top[v]=v;
dfs2(v,u);
}
}
然后单点子树查询修改都很简单,对于链 \((x,y)\) 的操作,考虑每次将其链首深度大的跳到链首,如果跳 \(x,y\) 深度大的就会出现将 \(x\) 跳到 \(fa_1\) 的情况,处理起来比较麻烦。
复杂度的证明:
不难发现每跳一次重链的链首,链首到其父亲的边是一条轻边。
设当前经过的点的子树大小为 \(x\),则若经过一条轻边,\(x\) 至少翻倍。
所以一共只会经过 \(O(\log)\) 条轻边,\(O(\log)\) 条重链。
模板。
#include<bits/stdc++.h>
#define sd std::
#define int long long
#define F(i,a,b) for(int i=(a);i<=(b);i++)
#define ff(i,a,b) for(int i=(a);i>=(b);i--)
#define me(x,y) memset(x,y,sizeof x)
#define pii sd pair<int,int>
#define X first
#define Y second
#define dbg(x) sd cout<<#x<<":"<<x<<" "
#define dg(x) sd cout<<#x<<":"<<x<<"\n"
#define inf 1e10
int read(){int w=1,c=0;char ch=getchar();for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;for(;ch>='0'&&ch<='9';ch=getchar()) c=(c<<3)+(c<<1)+ch-48;return w*c;}
void printt(int x){if(x>9) printt(x/10);putchar(x%10+48);}
void print(int x){if(x<0) putchar('-'),printt(-x);else printt(x);}
void put(int x){print(x);putchar('\n');}
void printk(int x){print(x);putchar(' ');}
const int N=5e5+10,P=1e9+7;
int n,dfn[N],num,w[N],siz[N],top[N],son[N],da[N],dep[N],a[N];//son为其重儿子
sd vector<int> g[N];
struct seg_Tree
{
int s[N<<2],ma[N<<2];//维护和以及最大值
#define ls k<<1
#define rs k<<1|1
void pushup(int k)
{
s[k]=s[ls]+s[rs];
ma[k]=sd max(ma[ls],ma[rs]);
}
void built(int k,int l,int r)
{
if(l==r)
{
s[k]=ma[k]=a[l];
return;
}
int mid=l+r>>1;
built(ls,l,mid);
built(rs,mid+1,r);
pushup(k);
}
void update(int k,int l,int r,int x,int v)//单点修改
{
if(l==r)
{
a[l]=s[k]=ma[k]=v;
return;
}
int mid=l+r>>1;
if(x<=mid) update(ls,l,mid,x,v);
else update(rs,mid+1,r,x,v);
pushup(k);
}
pii ask(int k,int l,int r,int x,int y)
{
if(x<=l&&y>=r) return {s[k],ma[k]};
int mid=l+r>>1;
pii res={0,-inf},K;
if(x<=mid)
{
K=ask(ls,l,mid,x,y);
res={res.X+K.X,sd max(res.Y,K.Y)};
}
if(y>mid)
{
K=ask(rs,mid+1,r,x,y);
res={res.X+K.X,sd max(res.Y,K.Y)};
}
return res;
}
pii query(int x,int y)
{
pii k,res={0,-inf};
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]]) sd swap(x,y);
// dbg(top[x]),dg(x);
k=ask(1,1,n,dfn[top[x]],dfn[x]);
res={res.X+k.X,sd max(res.Y,k.Y)};
x=da[top[x]];
}
if(dep[x]<dep[y]) sd swap(x,y);
// dbg(y),dg(x);
k=ask(1,1,n,dfn[y],dfn[x]);
return {res.X+k.X,sd max(res.Y,k.Y)};
}
}T;
void dfs1(int u,int fa)
{
siz[u]=1;
for(auto v:g[u])
{
if(v==fa) continue;
dep[v]=dep[u]+1;
da[v]=u;
dfs1(v,u);
siz[u]+=siz[v];
if(siz[v]>siz[son[u]]) son[u]=v;
}
}
void dfs2(int u,int fa)
{
dfn[u]=++num;
if(son[u]) top[son[u]]=top[u],dfs2(son[u],u);
for(auto v:g[u])
{
if(v==fa||v==son[u]) continue;
top[v]=v;
dfs2(v,u);
}
}
void solve()
{
n=read();
F(i,2,n)
{
int x=read(),y=read();
g[x].emplace_back(y);
g[y].emplace_back(x);
}
F(i,1,n) w[i]=read();
dep[1]=da[1]=1;
dfs1(1,0);
top[1]=1;
dfs2(1,0);
F(i,1,n) a[dfn[i]]=w[i];
T.built(1,1,n);
int q=read();
while(q--)
{
char op[6];
scanf("%s",op);
int x=read(),y=read();
if(op[0]=='C')
{
T.update(1,1,n,dfn[x],y);
}
if(op[0]=='Q')
{
pii res=T.query(x,y);
if(op[1]=='M') put(res.Y);
else put(res.X);
}
}
}
signed main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
int T=1;
// T=read();
while(T--) solve();
return 0;
}

浙公网安备 33010602011771号