综述
在熟练泼粪树链剖分之前,我们先要引进新概念(不是某魔性英语教材):
重边:对于某非叶子节点u,其儿子v中满足v的子树最大的边(u,v)即为重边
轻边:一棵树上不是重边的边就是轻边
重链:连在一起的重边
怎样找到所有的重边呢?
我们通过两个dfs实现
//sz为子树大小,deep为深度,son为重儿子,len为重链长度,top为重链的顶端点
//treeid为从原点至偏移后的点,fromid为投射到原点
void dfs1(int u,int fat,int d){
deep[u]=d;
fa[u]=fat;
sz[u]=1;
for(int i=head[u];i;i=edge[i].nxt){
int v=edge[i].v;
if(v!=fat){
dfs1(v,u,d+1);
sz[u]+=sz[v];
if(son[u]==-1||sz[v]>sz[son[u]])
son[u]=v;
}
}
}
void dfs2(int u,int tp){
top[u]=tp;
idcnt++;
treeid[u]=idcnt;
fromid[idcnt]=u;
len[tp]++;
if(son[u]==-1)
return ;
dfs2(son[u],tp);
for(int i=head[u];i;i=edge[i].nxt){
int v=edge[i].v;
if(v!=fa[u]&&v!=son[u]){
dfs2(v,v);
}
}
}
所以,对于其中一个点u,区间[treeid[top[u]],treeid[top[u]+len[top[u]]]]代表一条重链
然后呢,对于每一条重链,可以开一个数据结构来维护
以上,便是树链剖分的概述
例题1 bzoj 1036 树的统计Count
一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。我们将以下面的形式来要求你对这棵树完成
一些操作: I. CHANGE u t : 把结点u的权值改为t ;
II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 ;
III. QSUM u v: 询问从点u到点v的路径上的节点的权值和.
注意:从点u到点v的路径上的节点包括u和v.
直接上代码吧
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
#define lch rt*2
#define rch rt*2+1
using namespace std;
const int N=100005,M=200005,T=200005;
const int INF=2147483647;
struct node{
int u,v,nxt;
}edge[M];
int head[N],mcnt;
int sz[N],top[N];
int son[N],deep[N],fa[N];
int treeid[N],formid[N],idcnt;
int val[N];
int n,q;
void add_edge(int u,int v){
mcnt++;
edge[mcnt].u=u;
edge[mcnt].v=v;
edge[mcnt].nxt=head[u];
head[u]=mcnt;
}
void dfs1(int u,int fat,int d){
sz[u]=1;
deep[u]=d;
fa[u]=fat;
for(int i=head[u];i;i=edge[i].nxt){
int v=edge[i].v;
if(v!=fat){
dfs1(v,u,d+1);
sz[u]+=sz[v];
if(!son[u]||sz[v]>sz[son[u]])
son[u]=v;
}
}
}
void dfs2(int u,int tp){
top[u]=tp;
idcnt++;
treeid[u]=idcnt;
formid[idcnt]=u;
if(!son[u])
return ;
dfs2(son[u],tp);
for(int i=head[u];i;i=edge[i].nxt){
int v=edge[i].v;
if(v!=fa[u]&&v!=son[u]){
dfs2(v,v);
}
}
}
//
struct mode{
int val,sum;
}tree[T*4];
void update(int rt){
tree[rt].val=max(tree[lch].val,tree[rch].val);
tree[rt].sum=tree[lch].sum+tree[rch].sum;
}
void build_tree(int rt,int l,int r){
if(l==r){
tree[rt].val=val[formid[l]];
tree[rt].sum=val[formid[l]];
return ;
}
int mid=(l+r)/2;
build_tree(lch,l,mid);
build_tree(rch,mid+1,r);
update(rt);
}
void change_tree(int rt,int l,int r,int pos,int v){
if(l==r){
tree[rt].val=v;
tree[rt].sum=v;
return ;
}
int mid=(l+r)/2;
if(pos<=mid)
change_tree(lch,l,mid,pos,v);
else
change_tree(rch,mid+1,r,pos,v);
update(rt);
}
int find_max_tree(int rt,int l,int r,int st,int ed){
if(st<=l&&r<=ed)
return tree[rt].val;
int mid=(l+r)/2;
int ans=-INF;
if(st<=mid)
ans=max(ans,find_max_tree(lch,l,mid,st,ed));
if(ed>mid)
ans=max(ans,find_max_tree(rch,mid+1,r,st,ed));
return ans;
}
int find_sum_tree(int rt,int l,int r,int st,int ed){
if(st<=l&&r<=ed)
return tree[rt].sum;
int mid=(l+r)/2;
int ans=0;
if(st<=mid)
ans+=find_sum_tree(lch,l,mid,st,ed);
if(ed>mid)
ans+=find_sum_tree(rch,mid+1,r,st,ed);
return ans;
}
//
void change(int pos,int v){
change_tree(1,1,n,treeid[pos],v);
}
int query_max(int x,int y){
int ans=-INF;
while(top[x]!=top[y]){
if(deep[top[x]]<deep[top[y]])
swap(x,y);
ans=max(ans,find_max_tree(1,1,n,treeid[top[x]],treeid[x]));
x=fa[top[x]];
}
if(deep[x]>deep[y])
swap(x,y);
ans=max(ans,find_max_tree(1,1,n,treeid[x],treeid[y]));
return ans;
}
int query_sum(int x,int y){
int ans=0;
while(top[x]!=top[y]){
if(deep[top[x]]<deep[top[y]]){
swap(x,y);
}
ans+=find_sum_tree(1,1,n,treeid[top[x]],treeid[x]);
x=fa[top[x]];
}
if(deep[x]>deep[y])
swap(x,y);
ans+=find_sum_tree(1,1,n,treeid[x],treeid[y]);
return ans;
}
//
void re0(){
memset(head,0,sizeof head);
mcnt=0;
memset(son,0,sizeof son);
idcnt=0;
}
void init(){
scanf("%d",&n);
for(int i=1;i<n;i++){
int u,v;
scanf("%d%d",&u,&v);
add_edge(u,v);
add_edge(v,u);
}
for(int i=1;i<=n;i++)
scanf("%d",&val[i]);
dfs1(1,-1,1);
dfs2(1,1);
build_tree(1,1,n);
}
void solve(){
char s[20];
int x,y;
scanf("%d",&q);
for(int i=1;i<=q;i++){
scanf("%s",s);
scanf("%d%d",&x,&y);
if(s[1]=='M'){
printf("%d\n",query_max(x,y));
}
else if(s[1]=='S')
printf("%d\n",query_sum(x,y));
else
change(x,y);
}
}
int main()
{
re0();
init();
solve();
}
bzoj2234 染色
给定一棵有n个节点的无根树和m个操作,操作有2类:
1、将节点a到节点b路径上所有点都染成颜色c;
2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段)
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
#define lch x*2
#define rch x*2+1
using namespace std;
const int N=100005,M=200005,T=200005;
const int INF=2147483647;
struct node{
int u,v,nxt;
}edge[M];
int head[N],mcnt;
int sz[N],top[N];
int son[N],deep[N],fa[N];
int treeid[N],fromid[N],idcnt;
int color[N];
int n,q;
void add_edge(int u,int v){
mcnt++;
edge[mcnt].u=u;
edge[mcnt].v=v;
edge[mcnt].nxt=head[u];
head[u]=mcnt;
}
void dfs1(int u,int fat,int d){
sz[u]=1;
deep[u]=d;
fa[u]=fat;
for(int i=head[u];i;i=edge[i].nxt){
int v=edge[i].v;
if(v==fat)
continue ;
dfs1(v,u,d+1);
sz[u]+=sz[v];
if(!son[u]||sz[v]>sz[son[u]]){
son[u]=v;
}
}
}
void dfs2(int u,int tp){
top[u]=tp;
idcnt++;
treeid[u]=idcnt;
fromid[idcnt]=u;
if(!son[u])
return ;
dfs2(son[u],tp);
for(int i=head[u];i;i=edge[i].nxt){
int v=edge[i].v;
if(v==fa[u]||v==son[u])
continue ;
dfs2(v,v);
}
}
//
struct mode{
int sum,lc,rc;
int ac;
int l,r;
}tree[T*4];
void update(int x){
if(tree[x].l==tree[x].r&&tree[x].l==0)
return ;
if(tree[x].ac!=-1){
tree[lch].ac=tree[x].ac;
tree[rch].ac=tree[x].ac;
tree[x].sum=0;
tree[x].lc=tree[x].rc=tree[x].ac;
tree[x].ac=-1;
}
else{
if(tree[lch].ac!=-1)
update(lch);
if(tree[rch].ac!=-1)
update(rch);
tree[x].sum=tree[lch].sum+tree[rch].sum;
if(tree[lch].rc!=tree[rch].lc)
tree[x].sum++;
if(tree[x].l!=tree[x].r){
tree[x].lc=tree[lch].lc;
tree[x].rc=tree[rch].rc;
}
}
}
void build_tree(int x,int l,int r){
tree[x].l=l,tree[x].r=r;
tree[x].lc=tree[x].rc=tree[x].ac=-1;
if(l==r){
tree[x].sum=0;
tree[x].lc=tree[x].rc=color[fromid[l]];
return ;
}
int mid=(l+r)/2;
build_tree(lch,l,mid);
build_tree(rch,mid+1,r);
update(x);
}
void change_tree(int x,int l,int r,int st,int ed,int c){
if(st<=l&&r<=ed){
tree[x].ac=c;
tree[x].lc=tree[x].rc=c;
tree[x].sum=0;
return ;
}
int mid=(l+r)/2;
update(x);
if(st<=mid)
change_tree(lch,l,mid,st,ed,c);
if(ed>mid)
change_tree(rch,mid+1,r,st,ed,c);
update(x);
}
int find_tree(int x,int l,int r,int st,int ed,int &lc,int &rc){
if(st<=l&&r<=ed){
update(x);
lc=tree[x].lc;
rc=tree[x].rc;
return tree[x].sum;
}
int mid=(l+r)/2;
int ans=0;
update(x);
int ll,lr,rl,rr;
ll=lr=rl=rr=-1;
if(st<=mid)
ans+=find_tree(lch,l,mid,st,ed,ll,lr);
if(ed>mid)
ans+=find_tree(rch,mid+1,r,st,ed,rl,rr);
if(rr==-1)
rr=lr;
if(ll==-1)
ll=rl;
if(rl!=-1&&lr!=-1&&lr!=rl)
ans++;
lc=ll,rc=rr;
update(x);
return ans;
}
//
void C(int x,int y,int c){
while(top[x]!=top[y]){
if(deep[top[x]]<deep[top[y]]){
swap(x,y);
}
change_tree(1,1,n,treeid[top[x]],treeid[x],c);
x=fa[top[x]];
}
if(deep[x]>deep[y])
swap(x,y);
change_tree(1,1,n,treeid[x],treeid[y],c);
}
int Q(int x,int y){
int ch[2],ah[2];
ch[0]=ch[1]=-1;
int op=0;
int ans=1;
while(top[x]!=top[y]){
if(deep[top[x]]<deep[top[y]]){
swap(x,y);
op^=1;
}
ans+=find_tree(1,1,n,treeid[top[x]],treeid[x],ah[op],ah[op^1]);
if(ah[op^1]!=-1&&ah[op^1]!=ch[op]&&ch[op]!=-1)
ans++;
ch[op]=ah[op];
x=fa[top[x]];
}
op^=1;
if(deep[x]>deep[y]){
swap(x,y);
op^=1;
}
ans+=find_tree(1,1,n,treeid[x],treeid[y],ah[op],ah[op^1]);
if(ah[op^1]!=ch[op]&&ch[op]!=-1&&ah[op^1]!=-1)
ans++;
op^=1;
if(ah[op^1]!=ch[op]&&ch[op]!=-1&&ah[op^1]!=-1)
ans++;
return ans;
}
//
void init(){
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++)
scanf("%d",&color[i]);
for(int i=1;i<n;i++){
int u,v;
scanf("%d%d",&u,&v);
add_edge(u,v);
add_edge(v,u);
}
dfs1(1,-1,1);
dfs2(1,1);
build_tree(1,1,n);
}
void solve(){
char s[10];
int x,y,z;
for(int i=1;i<=q;i++){
scanf("%s",s);
if(s[0]=='Q'){
scanf("%d%d",&x,&y);
printf("%d\n",Q(x,y));
}
else if(s[0]=='C'){
scanf("%d%d%d",&x,&y,&z);
C(x,y,z);
}
}
}
int main()
{
init();
solve();
}