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

    //树链剖分 
    //将最短路上的区间分成若干条链 使得每一条链上的操作都能用线段树解决 
    //分轻重 重儿子为某节点的儿子中儿子最多的子节点 重儿子之间的边为重边 重边相连即为重链 
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    int n,m,root,MOD,dot[100001],dotid[100001];//dot[]儿子编号 dotid[]儿子新编号 
    int cnt,head[100001];
    struct uio{
        int to,next;
    }edge[200001];//链式前向星 
    int seg[400001],lazy[400001];//线段树数组 
    int hvyson[100001],id[100001],fa[100001],dfsno,dep[100001],siz[100001],top[100001];
    //hvyson[]重儿子编号 id[]新编号(新dfs序) fa[]父节点 dfsno为dfs序 dep[]深度 siz[]子树大小 top[]当前链顶端节点 
    void add(int x,int y)
    {
        edge[++cnt].next=head[x];
        edge[cnt].to=y;
        head[x]=cnt;
    }
    
    //--------------------------------------------------以下为线段树 
    void pushdown(int now,int len)
    {
        int lson=now<<1,rson=(now<<1)|1;
        lazy[lson]+=lazy[now];
        lazy[rson]+=lazy[now];
        seg[lson]+=lazy[now]*(len-(len>>1));
        seg[rson]+=lazy[now]*(len>>1);
        seg[lson]%=MOD;
        seg[rson]%=MOD;
        lazy[now]=0;
    }
    void build(int now,int l,int r)
    {
        if(l==r)
        {
            seg[now]=dotid[l];
            seg[now]%=MOD;
            return;
        }
        int mid=(l+r)/2;
        build(now<<1,l,mid);
        build((now<<1)|1,mid+1,r);
        seg[now]=(seg[now<<1]+seg[(now<<1)|1])%MOD;
    }
    int query(int now,int l,int r,int L,int R)
    {
        int ans=0;
        if(L<=l&&r<=R)
        {
            ans+=seg[now];
            return ans%MOD;
        }
        pushdown(now,r-l+1);
        int mid=(l+r)/2;
        if(L<=mid)
            ans+=query(now<<1,l,mid,L,R);
        if(mid+1<=R)
            ans+=query((now<<1)|1,mid+1,r,L,R);
        return ans%MOD;
    }
    void add_update(int now,int l,int r,int L,int R,int z)
    {
        if(L<=l&&r<=R)
        {
            lazy[now]+=z;
            seg[now]+=z*(r-l+1);
            return;
        }
        pushdown(now,r-l+1);
        int mid=(l+r)/2;
        if(L<=mid)
            add_update(now<<1,l,mid,L,R,z);
        if(mid+1<=R)
            add_update((now<<1)|1,mid+1,r,L,R,z);
        seg[now]=(seg[now<<1]+seg[(now<<1)|1])%MOD;
    }
    //--------------------------------------------------以上为线段树 
    
    void dfs1(int x,int f,int depth)//求普通dfs序 目的计算子树大小 
    {
        dep[x]=depth;//标记深度 
        fa[x]=f;//标记父亲 
        siz[x]=1;//标记子树大小 
        int maxson=-1;//记录重儿子的儿子数 
        for(int i=head[x];i;i=edge[i].next)
        {
            int y=edge[i].to;
            if(y==f)
                continue;
            dfs1(y,x,depth+1);
            siz[x]+=siz[y];//把它的儿子的儿子数加到它身上 
            if(siz[y]>maxson)
            {
                hvyson[x]=y;//标记重儿子 
                maxson=siz[y];
            }
         }
    }
    void dfs2(int x,int topf)//topf为当前重链的最顶端节点 
    {
        id[x]=++dfsno;//标记每个点的新编号(新dfs序) 
        dotid[dfsno]=dot[x];//把点值赋值到新编号上 
        top[x]=topf;//标记重链顶端 
        if(!hvyson[x])//没有重儿子就返回 
            return;
        dfs2(hvyson[x],topf);//优先dfs重儿子 
        for(int i=head[x];i;i=edge[i].next)
        {
            int y=edge[i].to;
            if(y==fa[x]||y==hvyson[x])
                continue;
            dfs2(y,y);//对于每个轻儿子其重链顶端都为自己 
        }
    }
    void update_section(int x,int y,int z)
    {
        z%=MOD;
        while(top[x]!=top[y])//若两个点不在同一条重链上 
        {
            if(dep[top[x]]<dep[top[y]])//将x点改为所在重链顶端深度更深的那个点 
                swap(x,y);
            add_update(1,1,n,id[top[x]],id[x],z);//先修改从x到x所在重链顶端的点的权值 
            x=fa[top[x]];//把x跳到x所在重链顶端的那个点 
        }
        //直至两点位于同一条重链上 
        if(dep[x]>dep[y])//把x改为深度更浅的那个点 
            swap(x,y);
        add_update(1,1,n,id[x],id[y],z);
    }
    int query_section(int x,int y)//注释全部同上 
    {
        int ans=0;
        while(top[x]!=top[y])
        {
            if(dep[top[x]]<dep[top[y]])
                swap(x,y);
            ans+=query(1,1,n,id[top[x]],id[x]);
            ans%=MOD;
            x=fa[top[x]];
        }
        if(dep[x]>dep[y])
            swap(x,y);
        ans+=query(1,1,n,id[x],id[y]);
        return ans%MOD;
    }
    void update_son(int x,int z)//直接用线段树处理 
    {
        add_update(1,1,n,id[x],id[x]+siz[x]-1,z);
    }
    int query_son(int x)//同上 
    {
        return query(1,1,n,id[x],id[x]+siz[x]-1);
    }
    void do_something()
    {
        for(int i=1;i<=m;i++)
        {
            int k,u,v,w;
            scanf("%d",&k);
            if(k==1)
            {
                scanf("%d%d%d",&u,&v,&w);
                update_section(u,v,w);
            }
            if(k==2)
            {
                scanf("%d%d",&u,&v);
                printf("%d
    ",query_section(u,v));
            }
            if(k==3)
            {
                scanf("%d%d",&u,&v);
                update_son(u,v);
            }
            if(k==4)
            {
                scanf("%d",&u);
                printf("%d
    ",query_son(u));
            }
        }
    }
    int main()
    {
        scanf("%d%d%d%d",&n,&m,&root,&MOD);
        for(int i=1;i<=n;i++)
            scanf("%d",&dot[i]);
        for(int i=1;i<n;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            add(u,v);
            add(v,u);
        }
        dfs1(root,0,1);
        dfs2(root,root);
        build(1,1,n);
        do_something();
         return 0;
    }
  • 相关阅读:
    添加yum源
    tar命令
    tomcat压力测试、优化
    vi命令
    Linux简单命令
    Linux简单命令
    vi命令
    tomcat压力测试、优化
    tar命令
    动态加载 CSS 文件
  • 原文地址:https://www.cnblogs.com/water-radish/p/9280657.html
Copyright © 2011-2022 走看看