树剖学习笔记
树链剖分
重链剖分
定义 重子节点 表示其子节点中子树最大的子结点。如果有多个子树最大的子结点,取其一。如果没有子节点,就无重子节点。
定义 轻子节点 表示剩余的所有子结点。
从这个结点到重子节点的边为 重边。
到其他轻子节点的边为 轻边。
若干条首尾衔接的重边构成 重链。
把落单的结点也当作重链,那么整棵树就被剖分成若干条重链。
接着我们用\(rnk\)表示\(DFS\)序对应的的节点编号,有\(rnk(dfn(x))=x\);
我们先用\(dfs1\)维护一些基本信息和重儿子。
void dfs(int x,int fat){
dep[x]=dep[fat]+1;
fa[x]=fat;siz[x]=1;
for(int k:G[x]){
if(k!=fat){
dfs(k,x);
siz[x]+=siz[k];
if(siz[k]>siz[bgs[x]]) bgs[x]=k;
}
}
}
void DFS(int x,int fat,int tp){
top[x]=tp;
if(bgs[x]){
DFS(bgs[x],x,tp);
}
for(int k:G[x]){
if(k!=fat&&k!=bgs[x]) DFS(k,x,k);
}
}
我们考虑\(dfs2\)的实现方法,显然我们先表示节点\(top\)表示为 所在 重链 的顶部节点(深度最小)。
而后我们先把重儿子所在路径上的所有\(top\)记为该点,不是重儿子的链我们考虑\(top=\)本身。
树上每个节点都属于且仅属于一条重链。
所有的重链将整棵树 完全剖分。
求LCA
不断向上跳重链,当跳到同一条重链上时,深度较小的结点即为 LCA。
向上跳重链时需要先跳所在重链顶端深度较大的那个。
int lca(int x,int y){
while(top[x]^top[y]) dep[top[x]]>dep[top[y]]?x=fa[top[x]]:y=fa[top[y]];
return dep[x]<dep[y]?x:y;
}
树的统计(树剖)
题意
求树上两点路径和,路径上的最大值,修改单点点权。
分析
就拿计算路径上和来举例子,首先我们有一个线段树上的\(query\_max\)函数,而后我们对于两个点\(x,y\)每次我们当他们所在链的\(top\)不相等时找到\(dep[top[x]]\)大的那个然后令\(ans+=\)这条路径上的\(sum\)而后我们让\(x\)跳到原来链的\(top\)的\(fa\)上,跳到另外一条链。直到\(top[x]=top[y]\)。而后我们再加上深度较低的那个点和较高那个点的路径上的和。又因为赋值\(dfn\)时我们在一条链上总有若\(dep[x]>dep[y]\)则\(dfn[x]>dfn[y]\)所以我们注意提交线段树处理时\(dfn[x]<dfn[y]\)即\(dep[x]<dep[y]\)。这样我们就可以得到最终的\(Ans\)了。
路径最大值同理。
树上单点修改直接线段树暴力修改即可注意在\(dfs\)时我们记录\(rnk[i]\)表示\(dfn\)为\(i\)的点编号是多少。这样我们可以方便的使用线段树进行修改。
代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 5e5 + 5;
#define mid ((l+r)>>1)
#define ls (k<<1)
#define rs (k<<1|1)
std::vector<int> G[MAXN << 1];
int top[MAXN << 1],cnt,dfn[MAXN<<1];
int fa[MAXN << 1], bgs[MAXN << 1], dep[MAXN << 1], siz[MAXN << 1] , rnk[MAXN<<1];
int w[MAXN];
struct tree{
void dfs(int x,int fat){
dep[x]=dep[fat]+1;
fa[x]=fat;siz[x]=1;
for(int k:G[x]){
if(k!=fat){
dfs(k,x);
siz[x]+=siz[k];
if(siz[k]>siz[bgs[x]]) bgs[x]=k;
}
}
}
void DFS(int x,int fat,int tp){
dfn[x]=++cnt;
top[x]=tp;
rnk[cnt]=x;
if(bgs[x]){
DFS(bgs[x],x,tp);
}
for(int k:G[x]){
if(k!=fat&&k!=bgs[x]) DFS(k,x,k);
}
}
}_sp;
int n;
struct seg_tree{
int maxx[MAXN],sum[MAXN];
void pushup(int k,int l,int r){sum[k]=sum[ls]+sum[rs];maxx[k]=max(maxx[ls],maxx[rs]);}
void build(int k,int l,int r){
if(l==r) return sum[k]=maxx[k]=w[rnk[l]],void();
build(ls,l,mid),build(rs,mid+1,r);
pushup(k,l,r);
}
void modify(int k,int l,int r,int x,int val){
if(l==r) return sum[k]=maxx[k]=val,void();
if(x<=mid) modify(ls,l,mid,x,val);
else modify(rs,mid+1,r,x,val);
pushup(k,l,r);
}
int query_max(int k,int l,int r,int x,int y){
if(x<=l&&y>=r) return maxx[k];
int res=-0x3f3f3f3f;
if(x<=mid) res=max(res,query_max(ls,l,mid,x,y));
if(y>=mid+1) res=max(res,query_max(rs,mid+1,r,x,y));
return res;
}
int query_sum(int k,int l,int r,int x,int y){
if(x<=l&&y>=r) return sum[k];
int res=0;
if(x<=mid) res+=query_sum(ls,l,mid,x,y);
if(y>=mid+1) res+=query_sum(rs,mid+1,r,x,y);
return res;
}
}T;
int tree_query_max(int x,int y){
int ans = -40000;
while (top[x] != top[y])
{
if (dep[top[x]] < dep[top[y]]) swap(x, y);
ans = max(ans, T.query_max(1, 1, n, dfn[top[x]], dfn[x]));
x = fa[top[x]];
}
if (dep[x] > dep[y]) swap(x, y);
ans = max(ans, T.query_max(1, 1, n, dfn[x], dfn[y]));
return ans;
}
int tree_query_sum(int x,int y){
int ans = 0;
while (top[x] != top[y])
{
if (dep[top[x]] < dep[top[y]]) swap(x, y);
ans+=T.query_sum(1, 1, n, dfn[top[x]], dfn[x]);
x = fa[top[x]];
}
if (dep[x] > dep[y]) swap(x, y);
ans+=T.query_sum(1, 1, n, dfn[x], dfn[y]);
return ans;
}
int main(){
scanf("%d", &n);
for (int i = 1, u, v; i < n; ++i)
{
scanf("%d%d", &u, &v);
G[u].push_back(v), G[v].push_back(u);
}
for (int i = 1; i <= n; ++i) scanf("%d", w + i);
_sp.dfs(1, 0);
_sp.DFS(1,0,1);
T.build(1, 1, n);
int q;
scanf("%d", &q);
char s[10]; int x, y;
while (q--)
{
scanf("%s%d%d", s, &x, &y);
if (s[0] == 'C') T.modify(1, 1, n, dfn[x], y);
else if (s[1] == 'M')
{
printf("%d\n",tree_query_max(x,y));
}
else
{
printf("%d\n",tree_query_sum(x,y));
}
}
return 0;
}