zoukankan      html  css  js  c++  java
  • LOJ#139. 树链剖分

    LOJ#139. 树链剖分

    题目描述

    这是一道模板题。

    给定一棵$n$个节点的树,初始时该树的根为 1 号节点,每个节点有一个给定的权值。下面依次进行 m 个操作,操作分为如下五种类型:

    • 换根:将一个指定的节点设置为树的新根。

    • 修改路径权值:给定两个节点,将这两个节点间路径上的所有节点权值(含这两个节点)增加一个给定的值。

    • 修改子树权值:给定一个节点,将以该节点为根的子树内的所有节点权值增加一个给定的值。

    • 询问路径:询问某条路径上节点的权值和。

    • 询问子树:询问某个子树内节点的权值和。

    输入格式

    第一行为一个整数 n,表示节点的个数。

    第二行 n 个整数表示第 i 个节点的初始权值 $a_i$​​。

    第三行 $n-1$ 个整数,表示 $i+1$号节点的父节点编号$f_{i+1}(1leqslant f_{i+1}leqslant n)$。

    第四行一个整数$m$,表示操作个数。

    接下来 $m$行,每行第一个整数表示操作类型编号:$(1 leqslant u, v leqslant n)$

    • 若类型为 $1$,则接下来一个整数 $u$,表示新根的编号。

    • 若类型为 $2$,则接下来三个整数 $u,v,k$,分别表示路径两端的节点编号以及增加的权值。

    • 若类型为 $3$,则接下来两个整数 $u,k$,分别表示子树根节点编号以及增加的权值。

    • 若类型为 $4$,则接下来两个整数$u,v$,表示路径两端的节点编号。

    • 若类型为 $5$,则接下来一个整数 $u$,表示子树根节点编号。

    输出格式

    对于每一个类型为 $4$ 或 $5$ 的操作,输出一行一个整数表示答案。

    样例

    样例输入

    6
    1 2 3 4 5 6
    1 2 1 4 4
    6
    4 5 6
    2 2 4 1
    5 1
    1 4
    3 1 2
    4 2 5

    样例输出

    15
    24
    19

    数据范围与提示

    对于 100% 的数据,$1leqslant n,m,k,a_ileqslant 10^5$​​。数据有一定梯度。


    题解Here!
    这个题如果没有换根就是一道沙茶题。。。
    如果是沙茶题我还会写博客吗。。。
    看到换根,想起了$LCT$,但是这个$LCT$还要维护子树信息,简直毒瘤啊。。。
    所以我们选择树链剖分+线段树。
    线段树是区间修改,区间加的利器!
    当然你真的要用$LCT$我也不拦着。。。
    没有换根很好做对吧。
    考虑换根对每个操作的影响。
    手玩几个小数据之后发现,如果我们以$1$为根建树,换根对$2,4$操作是没有影响的!
    所以直接套上板子。
    那子树怎么办?
    我们不是以$1$为根建树了吗?
    那就分类讨论一下$root$是否在子树内就好。
    1. 如果$x==root$,那就是修改/查询整棵树,直接$update(1,n),query(1,n)$就好。
    2. 如果$LCA(x,root)!=x$,即$id[x]>id[root] or id[root]>id[x]+size[x]-1$,那么修改/查询跟$root$没有关系,直接修改/查询$x$的子树$[id[x],id[x]+size[x]-1]$就好。
    3. 如果$LCA(x,root)==x$,即$id[x]<=id[root] and id[root]<=id[x]+size[x]-1$,这时比较麻烦。
    我们已经知道$deep[root]>deep[x]$了。
    那么我们发现,换完根后,所改变的只有$x$的所有子树中,包含$root$的那颗子树!
    那么我们可以找到$x$的所有子树中,包含$root$的那颗子树的根节点,也就是$x$的某个儿子,设为$y$。
    然后对除了$y$的子树$[id[y],id[y]+size[y]-1]$之外的所有节点进行修改/查询。
    也就是对$[1,id[y]-1],[id[y]+size[y],n]$这两个区间进行修改/查询。
    那怎么找$y$呢?
    我们可以从$root$开始暴力跳链,直到重链的顶端的父亲是$x$。
    如果重链的顶端的深度大于了$x$的深度,那么说明$x$与重链的顶端在一条重链上,直接返回$x$的重儿子$son[x]$即可。
    于是这个问题就完美解决了!
    妈妈再也不用担心树剖做不了换根了!
    $UPDATE$:原来的板子有错。。。
    但是我很好奇它是怎么过的。。。
    于是修改了一下。
    但是原来的$666ms$的提交没有了555.。。
    附代码:
    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #define LSON rt<<1
    #define RSON rt<<1|1
    #define DATA(x) b[x].data
    #define SIGN(x) b[x].c
    #define LSIDE(x) b[x].l
    #define RSIDE(x) b[x].r
    #define WIDTH(x) (RSIDE(x)-LSIDE(x)+1)
    #define MAXN 100010
    using namespace std;
    int n,m,c=1,d=1,root;
    int val[MAXN],head[MAXN],deep[MAXN],son[MAXN],size[MAXN],fa[MAXN],id[MAXN],pos[MAXN],top[MAXN];
    struct Tree{
        int next,to;
    }a[MAXN<<1];
    struct Segment_Tree{
        long long data,c;
        int l,r;
    }b[MAXN<<2];
    inline int read(){
        int date=0,w=1;char c=0;
        while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
        while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
        return date*w;
    }
    inline void pushup(int rt){
        DATA(rt)=DATA(LSON)+DATA(RSON);
    }
    inline void pushdown(int rt){
        if(!SIGN(rt)||LSIDE(rt)==RSIDE(rt))return;
        SIGN(LSON)+=SIGN(rt);
        DATA(LSON)+=SIGN(rt)*WIDTH(LSON);
        SIGN(RSON)+=SIGN(rt);
        DATA(RSON)+=SIGN(rt)*WIDTH(RSON);
        SIGN(rt)=0;
    }
    void buildtree(int l,int r,int rt){
        LSIDE(rt)=l;RSIDE(rt)=r;SIGN(rt)=0;
        if(l==r){
            DATA(rt)=val[pos[l]];
            return;
        }
        int mid=l+r>>1;
        buildtree(l,mid,LSON);
        buildtree(mid+1,r,RSON);
        pushup(rt);
    }
    void update(int l,int r,long long c,int rt){
        if(l<=LSIDE(rt)&&RSIDE(rt)<=r){
            SIGN(rt)+=c;
            DATA(rt)+=c*WIDTH(rt);
            return;
        }
        pushdown(rt);
        int mid=LSIDE(rt)+RSIDE(rt)>>1;
        if(l<=mid)update(l,r,c,LSON);
        if(mid<r)update(l,r,c,RSON);
        pushup(rt);
    }
    long long query(int l,int r,int rt){
        long long ans=0;
        if(l<=LSIDE(rt)&&RSIDE(rt)<=r)return DATA(rt);
        pushdown(rt);
        int mid=LSIDE(rt)+RSIDE(rt)>>1;
        if(l<=mid)ans+=query(l,r,LSON);
        if(mid<r)ans+=query(l,r,RSON);
        return ans;
    }
    inline void add(int x,int y){
        a[c].to=y;a[c].next=head[x];head[x]=c++;
        a[c].to=x;a[c].next=head[y];head[y]=c++;
    }
    void dfs1(int rt){
        son[rt]=0;size[rt]=1;
        for(int i=head[rt];i;i=a[i].next){
            int will=a[i].to;
            if(!deep[will]){
                deep[will]=deep[rt]+1;
                fa[will]=rt;
                dfs1(will);
                size[rt]+=size[will];
                if(size[will]>size[son[rt]])son[rt]=will;
            }
        }
    }
    void dfs2(int rt,int f){
        id[rt]=d++;pos[id[rt]]=rt;top[rt]=f;
        if(son[rt])dfs2(son[rt],f);
        for(int i=head[rt];i;i=a[i].next){
            int will=a[i].to;
            if(will!=fa[rt]&&will!=son[rt])dfs2(will,will);
        }
    }
    int check(int x){
        if(x==root)return -1;
        if(id[x]<=id[root]&&id[root]<=id[x]+size[x]-1){
            int y=root;
            while(deep[y]>deep[x]){
                if(fa[top[y]]==x)return top[y];
                y=fa[top[y]];
            }
            return son[x];
        }
        return 0;
    }
    void update_path(int x,int y,int k){
        while(top[x]!=top[y]){
            if(deep[top[x]]<deep[top[y]])swap(x,y);
            update(id[top[x]],id[x],k,1);
            x=fa[top[x]];
        }
        if(deep[x]>deep[y])swap(x,y);
        update(id[x],id[y],k,1);
    }
    void update_subtree(int x,int k){
        int y=check(x);
        if(y==-1)update(1,n,k,1);
        else if(y==0)update(id[x],id[x]+size[x]-1,k,1);
        else{
            update(1,n,k,1);
            update(id[y],id[y]+size[y]-1,-k,1);
        }
    }
    void query_path(int x,int y){
        long long s=0;
        while(top[x]!=top[y]){
            if(deep[top[x]]<deep[top[y]])swap(x,y);
            s+=query(id[top[x]],id[x],1);
            x=fa[top[x]];
        }
        if(deep[x]>deep[y])swap(x,y);
        s+=query(id[x],id[y],1);
        printf("%lld
    ",s);
    }
    void query_subtree(int x){
        long long s;
        int y=check(x);
        if(y==-1)s=query(1,n,1);
        else if(y==0)s=query(id[x],id[x]+size[x]-1,1);
        else s=query(1,n,1)-query(id[y],id[y]+size[y]-1,1);
        printf("%lld
    ",s);
    }
    void work(){
        int f,x,y,k;
        while(m--){
            f=read();x=read();
            switch(f){
                case 1:root=x;break;
                case 2:{
                    y=read();k=read();
                    update_path(x,y,k);
                    break;
                }
                case 3:{
                    k=read();
                    update_subtree(x,k);
                    break;
                }
                case 4:{
                    y=read();
                    query_path(x,y);
                    break;
                }
                case 5:query_subtree(x);break;
            }
        }
    }
    void init(){
        int x;
        n=read();
        for(int i=1;i<=n;i++)val[i]=read();
        for(int i=2;i<=n;i++){
            x=read();
            add(i,x);
        }
        m=read();
        root=1;
        deep[1]=1;
        dfs1(1);
        dfs2(1,1);
        buildtree(1,n,1);
    }
    int main(){
        init();
        work();
        return 0;
    }
    
  • 相关阅读:
    开源项目
    [Accessibility] Missing contentDescription attribute on image [可取行]失踪contentDescription属性图像
    Android 布局 中实现适应屏幕大小及组件滚动
    EF 错误记录
    EasyUI 加载时需要显示和隐藏 panel(面板)内容破版问题
    IE 报表缩放后页面破版
    VS 2017 引入nuget 问题
    SSRS 报表显示页面 asp net session丢失或者找不到 asp net session has expired or could not be found()
    log4net 配置
    网站
  • 原文地址:https://www.cnblogs.com/Yangrui-Blog/p/9472555.html
Copyright © 2011-2022 走看看