zoukankan      html  css  js  c++  java
  • HDU 3966(树链剖分)

    传送门

    题意:

    给你一个有nn个顶点的树,树上的每一个点都有一个点权,现在有33种操作:

    • I u v kI~ u~v~k,代表将结点uu到结点vv的最近的路径上的所有点的点权增加kk
    • D u v kD~ u~v~k ,代表将结点uu到结点vv的最近的路径上的所有点的点权减少kk
    • Q uQ~ u,代表求出结点uu的点权。

    题目分析:

    树链剖分的模板题。

    倘若只有操作11和操作22,则我们只需要用树上差分用O(n+m)mathcal{O}(n+m)的时间复杂度完成操作。

    但是,现在这个问题中,因此操作33的存在,使得用树上差分去做的话时间复杂度将会不优。

    因此我们可以考虑采用树链剖分。

    我们将树上的重链剖分出来之后,获取出他们的dfsdfs序,并用数据结构(线段树/树状数组)去维护区间的和即可。

    代码:

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=50005;
    struct Node{
        int to,next;
    }q[100005];
    struct ST{
        int sum,len,lazy;
    }tr[maxn<<2];
    int head[maxn],cnt=0,tot=0,f[maxn],dis[maxn],son[maxn],size[maxn],top[maxn];
    int id[maxn],rk[maxn],v[maxn];
    void add_edge(int from,int to){
        q[cnt].to=to;
        q[cnt].next=head[from];
        head[from]=cnt++;
    }
    void dfs1(int x){
        size[x]=1,dis[x]=dis[f[x]]+1;
        for(int i=head[x];i!=-1;i=q[i].next){
            int to=q[i].to;
            if(to==f[x]) continue;
            f[to]=x;
            dfs1(to);
            size[x]+=size[to];
            if(size[to]>size[son[x]]){
                son[x]=to;
            }
        }
    }
    void dfs2(int x,int t){
        top[x]=t;
        id[x]=++tot;
        rk[tot]=x;
        if(son[x]) dfs2(son[x],t);
        for(int i=head[x];i!=-1;i=q[i].next){
            int to=q[i].to;
            if(to==son[x]||to==f[x]) continue;
            dfs2(to,to);
        }
    }
    void push_up(int rt){
        tr[rt].sum=tr[rt<<1].sum+tr[rt<<1|1].sum;
    }
    void push_down(int rt){
        if(tr[rt].lazy){
            tr[rt<<1].sum+=1ll*tr[rt].lazy*tr[rt<<1].len;
            tr[rt<<1|1].sum+=1ll*tr[rt].lazy*tr[rt<<1|1].len;
            tr[rt<<1].lazy=tr[rt<<1].lazy+tr[rt].lazy;
            tr[rt<<1|1].lazy=tr[rt<<1|1].lazy+tr[rt].lazy;
            tr[rt].lazy=0;
        }
    }
    void build(int l,int r,int rt){
        tr[rt].lazy=0;
        tr[rt].len=r-l+1;
        if(l==r){
            tr[rt].sum=v[rk[l]];
            return ;
        }
        int mid=(l+r)>>1;
        build(l,mid,rt<<1);
        build(mid+1,r,rt<<1|1);
        push_up(rt);
    }
    void update(int L,int R,int l,int r,int rt,int k){
        if(L<=l&&R>=r){
            tr[rt].lazy=tr[rt].lazy+k;
            tr[rt].sum=tr[rt].sum+tr[rt].len*k*1l;
            return ;
        }
        push_down(rt);
        int mid=(l+r)>>1;
        if(L<=mid) update(L,R,l,mid,rt<<1,k);
        if(R>mid) update(L,R,mid+1,r,rt<<1|1,k);
        push_up(rt);
    }
    int query(int L,int R,int l,int r,int rt){
        if(L<=l&&R>=r){
            return tr[rt].sum;
        }
        push_down(rt);
        int mid=(l+r)>>1;
        int res=0;
        if(L<=mid) res+=query(L,R,l,mid,rt<<1);
        if(R>mid) res+=query(L,R,mid+1,r,rt<<1|1);
        return res;
    }
    int cal(int x,int y){
        int res=0;
        while(top[x]!=top[y]){
            if(dis[top[x]]<dis[top[y]]) swap(x,y);
            res+=query(id[top[x]],id[x],1,tot,1);
            x=f[top[x]];
        }
        if(id[x]>id[y]) swap(x,y);
        res+=query(id[x],id[y],1,tot,1);
        return res;
    }
    void UPDATE(int x,int y,int c){
        while(top[x]!=top[y]){
            if(dis[top[x]]<dis[top[y]]) swap(x,y);
            update(id[top[x]],id[x],1,tot,1,c);
            x=f[top[x]];
        }
        if(id[x]>id[y]) swap(x,y);
        update(id[x],id[y],1,tot,1,c);
    }
    int main()
    {
        int n,m,t;
        while(~scanf("%d%d%d",&n,&m,&t)){
            memset(head,-1,sizeof(head));
            memset(dis,0,sizeof(dis));
            memset(son,0,sizeof(son));
            tot=0;
            cnt=0;
            for(int i=1;i<=n;i++) scanf("%d",&v[i]);
            for(int i=1;i<n;i++){
                int from,to;
                scanf("%d%d",&from,&to);
                add_edge(from,to);
                add_edge(to,from);
            }
            dfs1(1);
            dfs2(1,1);
            build(1,tot,1);
            while(t--){
                int x,y,z;
                char op[2];
                scanf("%s",op);
                if(op[0]=='I'){
                    scanf("%d%d%d",&x,&y,&z);
                    UPDATE(x,y,z);
                }
                if(op[0]=='D'){
                    scanf("%d%d%d",&x,&y,&z);
                    z=-z;
                    UPDATE(x,y,z);
                }
                if(op[0]=='Q'){
                    scanf("%d",&x);
                    int res=query(id[x],id[x],1,tot,1);
                    printf("%d
    ",res);
                }
            }
        }
        return 0;
    }
    
  • 相关阅读:
    一个高级的makefile文件
    poj3616题(动态规划),看了别人的提示,自己又写了一遍
    浅谈C++ IO标准库(1)
    https证书安装踩坑
    一个简单通知服务的开发和搭建
    WCF学习笔记
    线程(Thread)、线程池(ThreadPool)技术
    BackgroundWorker与线程使用
    使用ITextSharp生成PDF文件心得
    值类型与引用类型
  • 原文地址:https://www.cnblogs.com/Chen-Jr/p/11007149.html
Copyright © 2011-2022 走看看