• dfs序和树链剖分


    dfs序和树链剖分

    总结

    • dfs序和树链剖分是把树形结构映射成线性结构,然后通过例如线段树这样的数据结构维护区间信息
    • dfs序是对树上节点进行dfs排列生成新的编号,其优点在于每一棵树/子树的编号是连续的,这样可以方便的进行任意子树的修改
    • 树链剖分是一种以重链/重边作为dfs优先遍历,这样不单单能够对任意子树进行修改,还能对树上两节点之间的简单路径进行方便的修改。后者的操作是每次修改(区间更新)top[]更深的重链,最终使得两个节点在一条重链上,再修改这条重链。
      由于需要修改重链数目有限(不加证明的给出是(logn)),所以每次操作复杂度为(O({logn}^2))

    基本步骤

    1. 第一次dfs,预处理出树的size, 节点高度,重儿子,父亲节点
    2. 第二次dfs,以重边优先进行dfs,重新编号,同时处理出重链节点的top[]节点(重链头部的节点)
    3. 线段树维护即可

    reference

    blog0

    模板题

    模板
    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define db(x) cout<<"["<<#x<<"]="<<x<<endl
    const ll maxn=1e6+10;
    ll N,M;
    ll seg[maxn],lazy[maxn];
    ll a[maxn],b[maxn];
    ll dep[maxn],sz[maxn],son[maxn],fa[maxn];//for dfs1
    ll top[maxn],idx[maxn];// for dfs2
    vector<ll> G[maxn];
    ll tot;//用于重编号
    ll dfs1(ll now,ll fath,ll depth){
        dep[now]  =depth;
        sz[now] = 1;
        fa[now] = fath;
        ll maxson = -1;
        for(ll i=0;i<G[now].size();i++){
            ll v = G[now][i];
            if(v==fath) continue; //continue 写法不易出错
            sz[now]+=dfs1(v,now,depth+1);
            if(sz[v]>maxson){maxson = sz[v];son[now] = v;}
        }
        return sz[now];
    }
    void dfs2(ll now,ll topf){
        idx[now] = (++tot);
        b[tot] = a[now];//
        top[now] = topf;
        if(!son[now])return ;
        dfs2(son[now],topf);
        for(ll i=0;i<G[now].size();i++){
            ll v = G[now][i];
            if(!idx[v]) dfs2(v,v);
        }
    
    }
    void build(ll root,ll l,ll r){
        if(l==r){
            seg[root] = b[l];
            return ;
        }
        ll mid = (l+r)>>1;
        build(root<<1,l,mid);
        build(root<<1|1,mid+1,r);
        seg[root] = seg[root<<1]+seg[root<<1|1];
    }
    void push_down(ll root,ll l,ll r){
        if(lazy[root]){
            ll mid = (l+r)>>1;
            seg[root<<1] += (mid-l+1)*lazy[root];
            seg[root<<1|1]+=(r-mid)*lazy[root];
            lazy[root<<1] +=lazy[root];
            lazy[root<<1|1]+=lazy[root];
            lazy[root]=0;
        }
    }
    void pointAdd(ll root,ll l,ll r,ll pos,ll val){
        if(l==r){
            seg[root]+=val; return ;
        }
        push_down(root,l,r);
        ll mid = (l+r)>>1;
        if(pos<=mid) pointAdd(root<<1,l,mid,pos,val);
        else pointAdd(root<<1|1,mid+1,r,pos,val);
        seg[root] = seg[root<<1]+seg[root<<1|1];
    }
    void intervalAdd(ll root,ll l,ll r,ll x,ll y,ll val){
        if(x<=l&&r<=y){
            seg[root]+=val*(r-l+1);
            lazy[root]+=val;
            return ;
        }
        push_down(root,l,r);
        ll mid = (l+r)>>1;
        if(x<=mid) intervalAdd(root<<1,l,mid,x,y,val);
        if(y>mid) intervalAdd(root<<1|1,mid+1,r,x,y,val);
        seg[root] = seg[root<<1]+seg[root<<1|1];
    }
    ll intervalAsk(ll root,ll l, ll r,ll x,ll y){
        if(x<=l&&r<=y){
            return seg[root];
        }
        push_down(root,l,r);
        ll mid = (l+r)>>1;
        ll ans = 0;
        if(x<=mid) ans+=intervalAsk(root<<1,l,mid,x,y);
        if(y>mid) ans+=intervalAsk(root<<1|1,mid+1,r,x,y);
        return ans;
        
    }
    ll treeSum(ll x,ll y){
        ll ans = 0;
        while(top[x]!=top[y]){
            if(dep[top[x]]<dep[top[y]]) swap(x,y);
            ll t=intervalAsk(1,1,N,idx[top[x]],idx[x]);
            ans+=t;
            x = fa[top[x]];
        }
        if(dep[x]>dep[y]) swap(x,y);
        ans+=intervalAsk(1,1,N,idx[x],idx[y]);
        return ans;
    }
    int main(){
        scanf("%lld %lld",&N,&M);
        for(ll i=1;i<=N;i++)scanf("%lld",a+i);
        for(ll i=1;i<N;i++){
            ll u,v;
            scanf("%lld %lld",&u,&v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        dfs1(1,0,1);dfs2(1,1);
        build(1,1,N);
        while(M--){
            ll op,x,val;
            scanf("%lld",&op);
            if(op==1){
                scanf("%lld %lld",&x,&val);
                pointAdd(1,1,N,idx[x],val);
            }
            else if(op==2){
                scanf("%lld %lld",&x,&val);
                intervalAdd(1,1,N,idx[x],idx[x]+sz[x]-1,val);
            }
            else{
                scanf("%lld",&x);
                printf("%lld
    ",treeSum(1,x));
            }
    
        }
    }
    
  • 相关阅读:
    Python循环语句
    Python基本数据类型
    Python条件句句
    Python基础
    jmeter用户自定义变量和CSV可变参数压测、和多参数的使用
    App资源在线升级更新
    javaweb项目启动报错org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/cab-web]]
    mui移动端日期选择器的使用
    java把json字符串转成实体
    MUI集成个推实现消息推送
  • 原文地址:https://www.cnblogs.com/fridayfang/p/11097151.html
Copyright © 2020-2023  润新知