zoukankan      html  css  js  c++  java
  • luogu P3384 【模板】重链剖分

    参考博客:https://www.cnblogs.com/ivanovcraft/p/9019090.html

    #include<iostream>
    #include<cstdio>
    #define int long long
    using namespace std;
    const int maxn=1e5+10;
    struct edge
    {
        int next,to;
    } e[maxn*2];
    int head[maxn],cnt;
    struct node
    {
        int l,r,ls,rs,sum,lazy;
    } a[maxn*2];
    int n,m,r,rt,mod;
    int v[maxn];
    int f[maxn];//f[u]    保存结点u的父亲节点
    int d[maxn];//d[u]    保存结点u的深度值
    int son[maxn];//son[u]    保存重儿子
    int size[maxn];//size[u]    保存以u为根的子树节点个数
    int top[maxn];//top[u]    保存当前节点所在链的顶端节点
    int id[maxn];//id[u]    保存树中每个节点剖分以后的新编号(DFS的执行顺序)
    int rk[maxn];//rk[u]    保存当前dfs标号在树中所对应的节点
    void add(int x,int y)
    {
        e[++cnt].next=head[x];
        e[cnt].to=y;
        head[x]=cnt;
    }
    void dfs1(int x) //当前节点
    {
        size[x]=1;
        d[x]=d[f[x]]+1;
        for(int v,i=head[x]; i; i=e[i].next)
            if((v=e[i].to)!=f[x])
            {
                f[v]=x;
                dfs1(v);
                size[x]+=size[v];//子节点的size已被处理,用它来更新父节点的size
                if(size[son[x]]<size[v])
                    son[x]=v; //选取size最大的作为重儿子
            }
    }
    void dfs2(int x,int tp)//当前节点、重链顶端
    {
        top[x]=tp;
        id[x]=++cnt;//标记dfs序
        rk[cnt]=x;//序号cnt对应节点u
        if(son[x])
            dfs2(son[x],tp);
    /*我们选择优先进入重儿子来保证一条重链上各个节点dfs序连续,
    一个点和它的重儿子处于同一条重链,所以重儿子所在重链的顶端还是t*/
        for(int v,i=head[x]; i; i=e[i].next)
            if((v=e[i].to)!=f[x]&&v!=son[x])
                dfs2(v,v);//一个点位于轻链底端,那么它的top必然是它本身
    }
    inline void pushup(int x)
    {
        a[x].sum=(a[a[x].ls].sum+a[a[x].rs].sum)%mod;
    }
    void build(int l,int r,int x)
    {
        if(l==r)
        {
            a[x].sum=v[rk[l]],a[x].l=a[x].r=l;
            return;
        }
        int mid=l+r>>1;
        a[x].ls=cnt++,a[x].rs=cnt++;
        build(l,mid,a[x].ls),build(mid+1,r,a[x].rs);
        a[x].l=a[a[x].ls].l,a[x].r=a[a[x].rs].r;
        pushup(x);
    }
    inline int len(int x)
    {
        return a[x].r-a[x].l+1;
    }
    inline void pushdown(int x)
    {
        if(a[x].lazy)
        {
            int ls=a[x].ls,rs=a[x].rs,lz=a[x].lazy;
            (a[ls].lazy+=lz)%=mod,(a[rs].lazy+=lz)%=mod;
            (a[ls].sum+=lz*len(ls))%=mod,(a[rs].sum+=lz*len(rs))%=mod;
            a[x].lazy=0;
        }
    }
    void update(int l,int r,int c,int x)
    {
        if(a[x].l>=l&&a[x].r<=r)
        {
            (a[x].lazy+=c)%=mod,(a[x].sum+=len(x)*c)%=mod;
            return;
        }
        pushdown(x);
        int mid=a[x].l+a[x].r>>1;
        if(mid>=l)
            update(l,r,c,a[x].ls);
        if(mid<r)
            update(l,r,c,a[x].rs);
        pushup(x);
    }
    int query(int l,int r,int x)
    {
        if(a[x].l>=l&&a[x].r<=r)
            return a[x].sum;
        pushdown(x);
        int mid=a[x].l+a[x].r>>1,tot=0;
        if(mid>=l)
            tot+=query(l,r,a[x].ls);
        if(mid<r)
            tot+=query(l,r,a[x].rs);
        return tot%mod;
    }
    inline int sum(int x,int y)
    {
        int ret=0;
        while(top[x]!=top[y])//两点不在同一条重链
        {
            if(d[top[x]]<d[top[y]])
                swap(x,y);
            (ret+=query(id[top[x]],id[x],rt))%=mod;//线段树区间求和,处理这条重链的贡献
            x=f[top[x]];//将x设置成原链头的父亲结点,走轻边,继续循环
        }
        //循环结束,两点位于同一重链上,但两点不一定为同一点,所以我们还要统计这两点之间的贡献
        if(id[x]>id[y])
            swap(x,y);
        return (ret+query(id[x],id[y],rt))%mod;
    }
    inline void updates(int x,int y,int c)
    {
        while(top[x]!=top[y])
        {
            if(d[top[x]]<d[top[y]])
                swap(x,y);
            update(id[top[x]],id[x],c,rt);
            x=f[top[x]];
        }
        if(id[x]>id[y])
            swap(x,y);
        update(id[x],id[y],c,rt);
    }
    signed main()
    {
        scanf("%lld%lld%lld%lld",&n,&m,&r,&mod);
        for(int i=1; i<=n; i++)
            scanf("%lld",&v[i]);
        for(int x,y,i=1; i<n; i++)
        {
            scanf("%lld%lld",&x,&y);
            add(x,y),add(y,x);
        }
        cnt=0,dfs1(r),dfs2(r,r);
        cnt=0,build(1,n,rt=cnt++);
        for(int op,x,y,k,i=1; i<=m; i++)
        {
            scanf("%lld",&op);
            if(op==1)//将树从 xx 到 yy 结点最短路径上所有节点的值都加上 zz
            {
                scanf("%lld%lld%lld",&x,&y,&k);
                updates(x,y,k);
            }
            else if(op==2)//求树从 xx 到 yy 结点最短路径上所有节点的值之和。
            {
                scanf("%lld%lld",&x,&y);
                printf("%lld
    ",sum(x,y));
            }
            else if(op==3)//将以 xx 为根节点的子树内所有节点值都加上 zz
            {
                scanf("%lld%lld",&x,&y);
                update(id[x],id[x]+size[x]-1,y,rt);
            }
            else//以 xx 为根节点的子树内所有节点值之和
            {
                scanf("%lld",&x);
                printf("%lld
    ",query(id[x],id[x]+size[x]-1,rt));
            }
        }
        return 0;
    }
  • 相关阅读:
    JavaScript实现的7种排序算法
    ECMAScript 2021 正式确认
    win10激活方法
    数据结构之Set | 让我们一块来学习数据结构
    使用jenkins一键打包发布vue项目
    数据结构之LinkedList | 让我们一块来学习数据结构
    数据结构之Queue | 让我们一块来学习数据结构
    数据结构之Stack | 让我们一块来学习数据结构
    数据结构之List | 让我们一块来学习数据结构
    JavaScript中的new,bind,call,apply的原理及简易实现
  • 原文地址:https://www.cnblogs.com/QingyuYYYYY/p/12312150.html
Copyright © 2011-2022 走看看