树链剖分模板 边权、点权区别

熟练剖分详解见:https://blog.csdn.net/tangzhide123yy/article/details/77532880
熟练剖分一般有如下几个步骤:
1.dfs1()求出fa,deep,size,son
2.dfs2()求出top,p
3.add()/Query ()树链剖分核心部分
4.updata()线段树更新操作(如单点修改,区间修改)
5.query()线段树查询(如区间查询,单点查询)

建立线段树(维护点权)

inline void build(int l,int r,int rt){//建立一棵线段树 
    tr[rt].l=l,tr[rt].r=r;
    if(l==r){tr[rt].sum=w[id[l]];return ;}
    //w[]存储的为原始的点权,写为w[id[l]]是因为dfs时的顺序不一样
    int midd=l+(r-l)/2;
    build(l,midd,ls),build(midd+1,r,rs);
    pushup(rt);
}

dfs1()维护点权模板

inline void dfs1(int x,int fat,int dep) {
    //这里应该就是树链剖分的第一个操作,把树上每个节点的size,fa等统统搞出来
    deep[x]=dep,fa[x]=fat,sz[x]=1;//fat表示x的爸爸 
    for(int i=0;i.size();i++){
        int v=g[x][i];
        if(v!=fat){
            dfs1(v,x,dep+1);
            sz[x]+=sz[v];
//son[x]记录x的儿子中的重点 
        }
    }
}

dfs2()维护点权模板

inline void dfs2(int x,int tp){//这个操作应该就是处理轻链和重链 
    top[x]=tp;pos[x]=++cnt;id[pos[x]]=x;
    //pos记录的是遍历到该点时的时间,id记录的是当时间为pos[x]时的编号是x, 
    if(son[x]==-1) return ;//到了叶子节点就退出。 
    dfs2(son[x],tp);//继续递归,并且是处理的重链和重点 
    for(int i=0;i.size();i++){
        int v=g[x][i];
        if(v!=fa[x]&&v!=son[x]) dfs2(v,v);//处理轻链 
    }//tp记录的是重点的祖先节点编号 
}

修改操作(维护点权)

inline void update(int l,int r,int c,int rt){//区间、单点修改 
    if(l<=tr[rt].l&&tr[rt].r<=r){   
        tr[rt].sum=(tr[rt].sum+(c*(tr[rt].r-tr[rt].l+1)%mod))%mod;
        tr[rt].tag+=c%mod;
        return ;
    }
    pushdown(rt);//下传tag
    if(l<=mid) update(l,r,c,ls);
    if(r>mid) update(l,r,c,rs);
    pushup(rt);//跟新父亲
}

区间查询(维护点权)

inline ll query(int l,int r,int rt){//区间查询 
    if(l<=tr[rt].l&&tr[rt].r<=r) return tr[rt].sum;
    pushdown(rt);
    int ans=0;
    if(l<=mid) ans+=query(l,r,ls),ans%=mod;
    if(r>mid) ans+=query(l,r,rs),ans%=mod;
    return ans%mod;
}

树链剖分核心(维护点权)

inline ll add(int t1,int t2,int c,int ok){//ok为一个标记,表示是进行哪一个操作 
    ll u=t1,v=t2,ans=0;
    while(top[u]!=top[v]){//在不同链上的情况 
        if(deep[top[u]]>deep[top[v]]) swap(u,v); 
        if(!ok)update(pos[top[v]],pos[v],c,1);//这里是更新值,例如本题中的从a到b都加上某一个值 
        else ans+=query(pos[top[v]],pos[v],1),ans%=mod;
        v=fa[top[v]];//继续操作下去,直到这两个点爬到他们的公共节点 
    }
    if(deep[u]>deep[v]) swap(u,v);//处理在同一条链上的情况 
    if(!ok) update(pos[u],pos[v],c,1);
    //与维护边权不同,不像维护边权那样要写成query(pos[son[u]],pos[v],1);
    else ans+=query(pos[u],pos[v],1);
    return ans%=mod;
}



build()维护边权模板

void build(int l,int r,int rt) {
    tree[rt].l=l;tree[rt].r=r;
    if(l==r) {tree[rt].max=w[l];return;}
    /*维护边权,至于为什么是w[l],因为我们在dfs2中有记录每条边是什么时候搜索到的,
    并记录了下来,所以这里按照搜索时顺序放入线段树中*/
    int midd=(l+(r-l)/2);
    build(l,midd,ls);build(midd+1,r,rs);
    pushup(rt);
}

dfs1()维护边权模板

void dfs1(int x,int fat,int dep) {//第一遍dfs处理出重儿子
    deep[x]=dep,fa[x]=fat,sz[x]=1;
    for(register int i=head[x];i;i=bot[i].nx) {
        int v=bot[i].nd;
        if(v!=fat) {
            road[bot[i].id]=v;
    //这里与维护点权不同,因为我们要把边加入进线段树中,所以要记录第几条边连向的点的编号
            dfs1(v,x,dep+1);
            sz[x]+=sz[v];
            if(son[x]==0||sz[son[x]].co;
      //这里也与维护点权不同,需要记录下重边的权值,方便dfs2()的进行
            }
        }
    }
}

dfs2()维护边权

void dfs2(int x,int tp,int cost){
    top[x]=tp;pos[x]=++tot;id[tot]=x;w[tot]=cost;//w记录边权的权值
    if(son[x]==0) return ;
    dfs2(son[x],tp,son_cost[x]);
    for(register int i=head[x];i;i=bot[i].nx) {
        int v=bot[i].nd;
        if(v!=fa[x]&&v!=son[x]) dfs2(v,v,bot[i].co);
    }
}

单边修改边权

void updata(int rt,int l,int data) {
    if(tree[rt].l==tree[rt].r) { tree[rt].max=data; return ;}
    else {
        if(l<=mid) updata(ls,l,data);
        else updata(rs,l,data);
        pushup(rt);
        /*
        这里跟维护点权不一样,是因为我们在线段树的第i个区间就是搜索时的第i条边,
        所以查找时只要记录下l与mid的关系即可
        */
    }
}

查询区间最大边权

long long query(int l,int r,int rt) {
    if(l<=tree[rt].l&&tree[rt].r<=r) return tree[rt].max;
    long long ans=-0x3f3f3f3f;
    if(l<=mid) ans=max(ans,query(l,r,ls));
    if(r>mid) ans=max(ans,query(l,r,rs));
    pushup(rt);
    return ans;
}

树剖核心操作(维护边权)

long long Query(int t1, int t2,int data) {
    long long ans=-0x3f3f3f3f,u=t1,v=t2;
    if(t1==t2) return 0;
    while(top[u]!=top[v]) {
        if(deep[top[u]]>deep[top[v]]) swap(u,v);
        ans=max(ans,query(pos[top[v]],pos[v],1));
        v=fa[top[v]];
    }
    if(u==v) return ans;
    if(deep[u]>deep[v]) swap(u,v);
    ans=max(ans,query(pos[son[u]],pos[v],1));
    //如果在一条重链或轻链上时,在处理点权时这样写query(pos[u],pos[v],1),
    //可能与当初dfs1()时记录了road[bot[i].id]=v有关吧
    //边权时这样写query(pos[son[u]],pos[v],1)
    return ans;
}

你可能感兴趣的:(总结,模板,树链剖分,数据结构)