树链剖分学习笔记

树链剖分学习笔记

一.前置技能:

dfs序,lca and 线段树 

二.如何操作:

1.概念:(自己瞎想的)树链剖分,又叫重链剖分,是一种通过将树剖为轻重链来将树上问题转化为区间问题,解决树上两点间路径问题,以及子树问题的算法。

2.实现:

<1>.概念:

重儿子:非叶子节点儿子中,子树最大的一个。

轻儿子:非叶子节点儿子中,除重儿子以外的儿子。

而叶子节点一无所有

重链:连接非叶子节点,非叶子节点的重儿子,非叶子节点的重儿子的重儿子,非叶子节点的重儿子的重儿子的重儿子的路径…… 

轻边:非叶子节点与轻儿子间的连边。

<2>.性质:

重链(除了从根节点开始的)一定从一非叶子节点的轻儿子开始,到叶子节点结束。

重链之间以轻边相连。

每个点一定属于且只属于一条重链。

<3>.操作:

将一条重链看成一段区间,树上路径即可拆分为几条重链(几个区间)。

子树问题,可以利用dfs序的性质:任意一子树,可以表示为dfs序上一连续区间。

而如何解决 将一条重链看成一段区间dfs序 的问题?

答:有侧重的dfs序。

普通dfs序由非叶子节点x到其任意一个儿子,太浪费它的优秀性质了。

我们不妨先由x到它的重儿子,再由x到其它儿子,两个问题就都解决了。

(1).dfs1

预处理好父亲f[x],深度d[x],子树大小siz[x],重儿子son[x]。

 1 void dfs(int x,int fa)
 2 {
 3     f[x]=fa,d[x]=d[fa]+1,siz[x]=1; int maxa=-1,maxt=0;
 4     //注意:maxt要赋值为0,因为叶子节点的son[x]为0,表示不存在重儿子,不然在dfs2会死循环
 5     for(int i=head[x];i;i=e[i].nxt)
 6         if(e[i].to!=fa)
 7         {
 8             dfs(e[i].to,x),siz[x]+=siz[e[i].to];
 9             if(maxa<siz[e[i].to]) maxt=e[i].to,maxa=siz[maxt];
10         }
11     son[x]=maxt;
12 }

(2).dfs2:

处理出树上每个节点的dfs序num[x],用临时变量v[num[x]]储存线段树上区间[num[x],num[x]]的权值,top[x]表示x所属重链的顶端节点编号。

1 void dfs2(int x,int topx)
2 {
3     num[x]=++tot,v[tot]=w[x],top[x]=topx;
4     if(son[x]) dfs2(son[x],topx);
5     //优先搜素重儿子,若是叶子节点无重儿子则不搜,不然会死循环。
6     for(int i=head[x];i;i=e[i].nxt) if(e[i].to!=f[x]&&e[i].to!=son[x]) dfs2(e[i].to,e[i].to);
7 }

(3).树剖lca

是树上两点间修改和询问操作的基础,每次跳一条重链,在重链这段连续区间上修改或询问。

 1 void LJ_update(int x,int y,int z)
 2 {
 3     while(top[x]!=top[y])
 4     {
 5         if(d[top[x]]<d[top[y]]) swap(x,y);
 6         update(1,n,num[top[x]],num[x],1,z);
 7         //顶部节点top[x]的dfs序一定小于x
 8         x=f[top[x]]; 
 9         //跳到上一条重链
10     }
11     if(d[x]>d[y]) swap(x,y);
12     update(1,n,num[x],num[y],1,z);
13 }
14 int LJ_query(int x,int y)
15 {
16     int ans=0;
17     while(top[x]!=top[y])
18     {
19         if(d[top[x]]<d[top[y]]) swap(x,y);
20         ans=(ans+query(1,n,num[top[x]],num[x],1))%mod;
21         x=f[top[x]];
22     }
23     if(d[x]>d[y]) swap(x,y);
24     ans=(ans+query(1,n,num[x],num[y],1))%mod;
25     return ans;
26 }

(4).子树:

非叶子节点x的子树dfs序区间为[num[x],num[x]+siz[x]-1];

1 void ZS_update(int x,int y){update(1,n,num[x],num[x]+siz[x]-1,1,y);}
2 int ZS_query(int x){return query(1,n,num[x],num[x]+siz[x]-1,1);}

 

总代码:

  1 #include<bits/stdc++.h>
  2 #define re register
  3 using namespace std;
  4 const int N=1e5+6;
  5 int n,opt,t1,t2,t3,root,mod,m,cnt=0,tot=0;
  6 int num[N],head[N],d[N],f[N],siz[N],son[N],top[N],v[N],lazy[N<<2],w[N<<2];
  7 struct edge{
  8     int nxt,to;
  9 }e[N<<1];
 10 inline void add(int u,int v){e[++cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt;}
 11 inline int read()
 12 {
 13     int T=0,F=1; char ch=getchar();
 14     while(ch<'0'||ch>'9'){if(ch=='-') F=-1; ch=getchar();}
 15     while(ch>='0'&&ch<='9') T=(T<<3)+(T<<1)+(ch-48),ch=getchar();
 16     return F*T;
 17 }
 18 void dfs(int x,int fa)
 19 {
 20     f[x]=fa,d[x]=d[fa]+1,siz[x]=1; int maxa=-1,maxt=0;
 21     for(int i=head[x];i;i=e[i].nxt)
 22         if(e[i].to!=fa)
 23         {
 24             dfs(e[i].to,x),siz[x]+=siz[e[i].to];
 25             if(maxa<siz[e[i].to]) maxt=e[i].to,maxa=siz[maxt];
 26         }
 27     son[x]=maxt;
 28 }
 29 void dfs2(int x,int topx)
 30 {
 31     num[x]=++tot,v[tot]=w[x],top[x]=topx;
 32     if(son[x]) dfs2(son[x],topx);
 33     for(int i=head[x];i;i=e[i].nxt) if(e[i].to!=f[x]&&e[i].to!=son[x]) dfs2(e[i].to,e[i].to);
 34 }
 35 void build(int l,int r,int x)
 36 {
 37     if(l==r){w[x]=v[l]; return;}
 38     int mid=((l+r)>>1);
 39     build(l,mid,x<<1),build(mid+1,r,x<<1|1);
 40     w[x]=w[x<<1]+w[x<<1|1];
 41 }
 42 inline void pushdown(int x,int len)
 43 {
 44     if(lazy[x])
 45     {
 46         lazy[x<<1]=(lazy[x<<1]+lazy[x])%mod,lazy[x<<1|1]=(lazy[x<<1|1]+lazy[x])%mod;
 47         w[x<<1]=(w[x<<1]+(len-(len>>1))*lazy[x])%mod,w[x<<1|1]=(w[x<<1|1]+(len>>1)*lazy[x])%mod;
 48         lazy[x]=0;
 49     }
 50 }
 51 void update(int l,int r,int p,int q,int x,int y)
 52 {
 53     if(p<=l&&r<=q){lazy[x]=(lazy[x]+y)%mod,w[x]=(w[x]+(r-l+1)*y)%mod; return;}
 54     pushdown(x,r-l+1);
 55     int mid=((l+r)>>1);
 56     if(p<=mid) update(l,mid,p,q,x<<1,y);
 57     if(q>mid) update(mid+1,r,p,q,x<<1|1,y);
 58     w[x]=w[x<<1]+w[x<<1|1];
 59 }
 60 int query(int l,int r,int p,int q,int x)
 61 {
 62     if(p<=l&&r<=q) return w[x];
 63     pushdown(x,r-l+1);
 64     int mid=((l+r)>>1),ans=0;
 65     if(p<=mid) ans=query(l,mid,p,q,x<<1);
 66     if(q>mid) ans=(ans+query(mid+1,r,p,q,x<<1|1))%mod;
 67     return ans;
 68 }
 69 void LJ_update(int x,int y,int z)
 70 {
 71     while(top[x]!=top[y])
 72     {
 73         if(d[top[x]]<d[top[y]]) swap(x,y);
 74         update(1,n,num[top[x]],num[x],1,z);
 75         x=f[top[x]]; 
 76     }
 77     if(d[x]>d[y]) swap(x,y);
 78     update(1,n,num[x],num[y],1,z);
 79 }
 80 int LJ_query(int x,int y)
 81 {
 82     int ans=0;
 83     while(top[x]!=top[y])
 84     {
 85         if(d[top[x]]<d[top[y]]) swap(x,y);
 86         ans=(ans+query(1,n,num[top[x]],num[x],1))%mod;
 87         x=f[top[x]];
 88     }
 89     if(d[x]>d[y]) swap(x,y);
 90     ans=(ans+query(1,n,num[x],num[y],1))%mod;
 91     return ans;
 92 }
 93 void ZS_update(int x,int y){update(1,n,num[x],num[x]+siz[x]-1,1,y);}
 94 int ZS_query(int x){return query(1,n,num[x],num[x]+siz[x]-1,1);}
 95 int main()
 96 {
 97     n=read(),m=read(),root=read(),mod=read();
 98     for(re int i=1;i<=n;++i) w[i]=read();
 99     for(re int i=1;i<n;++i) t1=read(),t2=read(),add(t1,t2),add(t2,t1);
100     //建图 
101     dfs(root,0); dfs2(root,root);//预处理 
102     memset(w,0,sizeof(w)); build(1,n,1);//建线段树 
103     for(re int i=1;i<=m;++i)
104     {
105         opt=read(),t1=read();
106         switch(opt)
107         {
108             case 1:t2=read(),t3=read(),LJ_update(t1,t2,t3);break;
109             case 2:t2=read(),printf("%d\n",LJ_query(t1,t2));break;
110             case 3:t2=read(),ZS_update(t1,t2);break;
111             case 4:printf("%d\n",ZS_query(t1));break;
112         }
113     }
114     return 0;
115 }

 

posted @ 2019-08-02 12:03  lsoi_ljk123  阅读(195)  评论(0编辑  收藏  举报