zoukankan      html  css  js  c++  java
  • 树链剖分

    树链剖分有什么用

    我们经常会写到这样的毒瘤题目:给你一棵树,每次对树上的一条链进行操作。  

    例如洛咕:P3384 【模板】树链剖分

    实现方法

    那么我们怎么很快的处理这样的问题呢?

    学过倍增LCA的同学应该清楚,就算是倍增,也只能找出它的几代父亲,而不能对路径上的每一个点进行修改。

    我们发现对于树上链的操作是很复杂的,因为树上的节点储存是不连续的。

    然后你就能拍脑袋了。

    既然不连续的区间修改起来很麻烦,那么有没有方法把它变成连续的呢?

    我们发现对于一棵树,我们对节点的储存顺序是没有要求的。

    对于一个父节点,我们只需要知道他儿子们的储存位置;对于一个子节点,我们只要知道他父亲的储存位置,我们就可以存储这棵树了。

    那么我们可以通过给这棵树进行重标号,把树上的链化为连续的区间,我们就能很轻松地处理一段链的修改,查询操作了。

    下面有几个定义:

    1.重儿子:一个节点的儿子节点里面,以重儿子为根的子树大小最大。

    2.轻儿子:一个节点的儿子节点,除了唯一一个重儿子,其余儿子为轻儿子。

    3.重边:连接父亲和重儿子的边称为重边。

    4.轻边:连接父亲和轻儿子的边称为轻边。

    5.重链:只由重边构成的链称为重链。

    我们只要把一棵树上的重链全部按顺序储存,就能把树上的问题转化为区间上的问题。

    那么接下来用线段树就能轻松维护这个区间问题了。

    时间复杂度证明

    我们已经学会给树解剖了(雾)

    那么我们怎么知道按照重链解剖,时间复杂度会变得更优呢?

    1.每走过一个轻边,子树的节点数至少减少一半

    我们假设树上一个节点有$n$个儿子,那么由于重儿子子树大小是最大的,当轻儿子的子树大小最大的时候,轻儿子的子树大小应该为frac{1}{n}$。

    我们可以用抽屉原理证明。

    那么轻儿子子树大小之多为父亲的$frac{1}{2}$

    2.一条链上最多有$2logn$条重链

     这个也是很好证明的。(虽然是伪证)

    我们考虑什么时候链最多。

    我们先考虑半链。

    发现重链的数量只和轻边有关。

    因为重链的两端所连的节点不可能再连接其他的重边,不然长度就会增加。

    那么一段链上的重链数就等于轻边数$+1$。

    根据结论$1$,一个节点数为$n$的树,最多走$logn$次轻边,节点数就会变成$1$。

    换句话说,一条半链上最多有$logn$条轻边。

    那么一条全链上最多有$2logn$条重链。

    3.单次修改,查询的操作为$log^2n$

    我们每次查询可以修改一条链从链底到链顶的一段区间。

    根据结论$2$我们知道,单次修改的线段树操作次数为$logn$次,而线段树的时间复杂度为$logn$。

    总的时间复杂度就是$log^2n$

    具体的实现细节

    我们先对树进行预处理,把树重标号以及剖分出重链。

    这个预处理由两个dfs构成。

    第一个dfs,我们可以计算出以下几个值。

    1.每个节点的子树大小$siz[x]$

    2.每个节点的深度$dep[x]$

    3.每个节点的父亲$fa[x]$

    4.每个节点的重儿子$son[x]$

     1 void dfs1(int x,int pre,int deep){
     2     dep[x]=deep;siz[x]=1;
     3     for(int i=head[x];i;i=nxt[i]){
     4         if(ver[i]==pre)continue;
     5         fa[ver[i]]=x;
     6         dfs1(ver[i],x,deep+1);
     7         siz[x]+=siz[ver[i]];
     8         if(siz[son[x]]<siz[ver[i]])son[x]=ver[i];
     9     }
    10 }
    View Code

     然后第二个dfs,我们给每个节点重新标号。

    统计出以下数组。

    1.每个节点在线段树上的位置$id[x]$

    2.线段树某个位置上的权值$rw[x]$

    3.每个节点所在链的链顶节点$top[x]$

    为了保证重链上的点储存位置连续,我们要先对重链进行标号。

     1 void dfs2(int x,int ltp){
     2     id[x]=++cnt;
     3     a[cnt]=w[x];
     4     top[x]=ltp;
     5     if(son[x])dfs2(son[x],ltp);
     6     for(int i=head[x];i;i=nxt[i]){
     7         if(ver[i]==son[x]||ver[i]==fa[x])continue;
     8         dfs2(ver[i],ver[i]);
     9     }
    10 }
    View Code

    可能比较抽象,我们可以通过一张图来看。

    红色框住的为重链,黑色标号为原来的编号,棕色标号为线段树位置。

    我们很容易发现重链上的节点是连续的,子树内的节点是连续的。

    那么我们就很容易进行维护了。

    至于线段树部分,就不详细解释了。直接看代码就好了

    代码

      1 #include <bits/stdc++.h>
      2 using namespace std;
      3 const int N=1e5+100;
      4 int read(){
      5     char c;int num,f=1;
      6     while(c=getchar(),!isdigit(c))if(c=='-')f=-1;num=c-'0';
      7     while(c=getchar(), isdigit(c))num=num*10+c-'0';
      8     return f*num;
      9 }
     10 int mod=1e9+7;
     11 int n,m,r,p;
     12 int w[N],dep[N],fa[N],son[N],id[N],top[N];
     13 int head[N],nxt[N*2],ver[N*2],tot=1,siz[N],cnt=0;
     14 void up(int &x,int y){
     15     x+=y;
     16     if(x>=mod)x-=mod;
     17 }
     18 //线段树
     19 int laz[N*4],tree[N*4],a[N];
     20 /*void build(int l,int r,int rt){
     21     for(int i=1;i<=n;i++)tree[i]=a[i];
     22 }
     23 void modify(int l,int r,int L,int R,int rt,int x){
     24     for(int i=L;i<=R;i++)up(tree[i],x);
     25 }
     26 int query(int l,int r,int L,int R,int rt){
     27     int ans=0;
     28     for(int i=L;i<=R;i++)up(ans,tree[i]);
     29     return ans;
     30 }*/
     31 
     32 void update(int rt){
     33     tree[rt]=tree[rt<<1]+tree[rt<<1|1];
     34     if(tree[rt]>=mod)tree[rt]-=mod;
     35 }
     36 void build(int l,int r,int rt){
     37     if(l==r){tree[rt]=a[l]%mod;return ;}
     38     int mid=(l+r)>>1;
     39     build(l,mid,rt<<1);
     40     build(mid+1,r,rt<<1|1);
     41     update(rt);
     42 }
     43 void pushdown(int rt,int len){
     44     up(laz[rt<<1],laz[rt]);
     45     up(tree[rt<<1],laz[rt]*(len-(len>>1))%mod);
     46     up(laz[rt<<1|1],laz[rt]);
     47     up(tree[rt<<1|1],laz[rt]*(len>>1)%mod);
     48     laz[rt]=0;
     49 }
     50 int query(int l,int r,int L,int R,int rt){
     51     if(L<=l&&r<=R)return tree[rt];
     52     int mid=(l+r)>>1,ans=0;
     53     pushdown(rt,r-l+1);
     54     if(L<=mid)up(ans,query(l,mid,L,R,rt<<1));
     55     if(mid+1<=R)up(ans,query(mid+1,r,L,R,rt<<1|1));
     56     return ans;
     57 }
     58 void modify(int l,int r,int L,int R,int rt,int x){
     59     if(L<=l&&r<=R){
     60         up(tree[rt],x*(r-l+1)%mod);
     61         up(laz[rt],x);
     62         return ;
     63     }
     64     int mid=(l+r)>>1;
     65     pushdown(rt,r-l+1);
     66     if(L<=mid)modify(l,mid,L,R,rt<<1,x);
     67     if(mid+1<=R)modify(mid+1,r,L,R,rt<<1|1,x);
     68     update(rt);
     69 }
     70 
     71 
     72 void add_edge(int u,int v){
     73     ver[++tot]=v;nxt[tot]=head[u];head[u]=tot;
     74     ver[++tot]=u;nxt[tot]=head[v];head[v]=tot;
     75 }
     76 void dfs1(int x,int pre,int deep){
     77     dep[x]=deep;siz[x]=1;
     78     for(int i=head[x];i;i=nxt[i]){
     79         if(ver[i]==pre)continue;
     80         fa[ver[i]]=x;
     81         dfs1(ver[i],x,deep+1);
     82         siz[x]+=siz[ver[i]];
     83         if(siz[son[x]]<siz[ver[i]])son[x]=ver[i];
     84     }
     85 }
     86 void dfs2(int x,int ltp){
     87     id[x]=++cnt;
     88     a[cnt]=w[x];
     89     top[x]=ltp;
     90     if(son[x])dfs2(son[x],ltp);
     91     for(int i=head[x];i;i=nxt[i]){
     92         if(ver[i]==son[x]||ver[i]==fa[x])continue;
     93         dfs2(ver[i],ver[i]);
     94     }
     95 }
     96 void link_modify(int x,int y,int val){
     97     while(top[x]!=top[y]){
     98         if(dep[top[x]]<dep[top[y]])swap(x,y);
     99         modify(1,n,id[top[x]],id[x],1,val);
    100         x=fa[top[x]];
    101     }
    102     if(dep[x]<dep[y])swap(x,y);
    103     modify(1,n,id[y],id[x],1,val);
    104 }
    105 int link_query(int x,int y){
    106     int ans=0;
    107     while(top[x]!=top[y]){
    108         if(dep[top[x]]<dep[top[y]])swap(x,y);
    109         up(ans,query(1,n,id[top[x]],id[x],1));
    110         x=fa[top[x]];
    111     }
    112     if(dep[x]<dep[y])swap(x,y);
    113     up(ans,query(1,n,id[y],id[x],1));
    114     return ans;
    115 }
    116 void tree_modify(int x,int val){
    117     modify(1,n,id[x],id[x]+siz[x]-1,1,val%mod);
    118 }
    119 int tree_query(int x){
    120     return query(1,n,id[x],id[x]+siz[x]-1,1);
    121 }
    122 int main()
    123 {
    124     //freopen("data.in","r",stdin);
    125     n=read();m=read();
    126     r=read();mod=read();
    127     for(int i=1;i<=n;i++)w[i]=read();
    128     for(int i=1;i< n;i++)add_edge(read(),read());
    129     dfs1(r,r,1);dfs2(r,r);build(1,n,1);
    130     for(int i=1;i<=m;i++){
    131         int opt=read(),x,y,z;
    132         if(opt==1){
    133             x=read();y=read();z=read();
    134             link_modify(x,y,z);
    135         }else if(opt==2){
    136             x=read();y=read();
    137             printf("%d
    ",link_query(x,y));
    138         }else if(opt==3){
    139             x=read();y=read();
    140             tree_modify(x,y);
    141         }else if(opt==4){
    142             x=read();
    143             printf("%d
    ",tree_query(x));
    144         }
    145     }
    146     //cout<<siz[1]<<endl;
    147     /*for(int i=1;i<=10;i++)a[i]=i;
    148     build(1,10,1);
    149     modify(1,10,1,10,1,10);
    150     cout<<query(1,10,1,3,1)<<endl;
    151     modify(1,10,2,9,1,10);
    152     cout<<query(1,10,1,3,1)<<endl;
    153     modify(1,10,1,2,1,10);
    154     cout<<query(1,10,1,3,1)<<endl;*/
    155     return 0;
    156 }
    View Code
  • 相关阅读:
    Android事件机制之一:事件传递和消费
    Android单个控件占父控件宽度一半且水平居中
    Android IllegalArgumentException: Cannot draw recycled bitmaps解决方法
    Android视图篇之一:Android常见基本布局
    Android Nine-patch(.9.png)小结
    adb server is out of date. killing... ADB server didn't ACK解决方法
    Docker 下自定义安装 Tomcat
    Docker 删除 images
    SecureCRT 取消右击粘贴功能
    如何将不同业务模块产生的日志 分多文件记录
  • 原文地址:https://www.cnblogs.com/onglublog/p/9973830.html
Copyright © 2011-2022 走看看