树链剖分

题目链接
题意:给定一颗树,树上每一个节点都有一个权值,然后有q次查询,
I a b c a节点和b节点之间的路径上的点的权值加上c
D a b c a节点和b节点之间的路径上的点的权值减上c
Q a 查询a节点的权值。
树链剖分是将树按照重链分成许多链,变成一个线性结构,这样就可以直接用线段树等数据结构处理了,每两个点之间链的条数不会超过log(n)条,如果用线段树处理的话,每一次的复杂度最多是log(n)*log(n)。
名词解释:重链,u的子节点为v,size[u]代表以u为根节点的子树节点数,则u的子节点最大的size[v]对应的这个v点就是u的重儿子,u->v之间的链就代表一条重链,然后将所有的重链链接起来重新编号解会变成一个线性结构。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
const int maxn=2e5+10;
int w[maxn],head[maxn],cont,tim,n,m,q;
void init()
{
    memset(head,-1,sizeof(head));
    cont=0;
    tim=0;
}
struct zp
{
    int u,v,next;
}node[maxn];
void add(int u,int v)
{
    node[cont].u=u,node[cont].v=v;
    node[cont].next=head[u];
    head[u]=cont++;
}
//   节点大小   节点深度    重儿子       节点所在链的顶端节点  节点父节点    节点剖分后的新节点编号  线段树中的节点代表的原树中节点号
int siz[maxn],dep[maxn],son[maxn],top[maxn],                  fa[maxn],     id[maxn],                       val[maxn];
void dfs1(int u,int f,int d)//处理处重链
{
    son[u]=-1;
    dep[u]=d;
    siz[u]=1;
    fa[u]=f;
    for(int i=head[u];i+1;i=node[i].next)
    {
        int v=node[i].v;
        if(v==f) continue;
        dfs1(v,u,d+1);
        siz[u]+=siz[v];
        if(son[u]==-1||siz[v]>siz[son[u]])
            son[u]=v;
    }
}
void dfs2(int u,int tp)//给重链编号对应到线段树上
{
    top[u]=tp;
    id[u]=++tim;
    val[id[u]]=u;
    if(son[u]==-1) return ;
    dfs2(son[u],tp);
    for(int i=head[u];i+1;i=node[i].next)
    {
        int v=node[i].v;
        if(v!=son[u]&&v!=fa[u])
            dfs2(v,v);
    }
}
struct tree
{
    int lazy,val;
}T[maxn];
void push_down(int k)
{
    if(T[k].lazy)
    {
        T[k<<1].val+=T[k].lazy;
        T[k<<1|1].val+=T[k].lazy;
        T[k<<1].lazy+=T[k].lazy;
        T[k<<1|1].lazy+=T[k].lazy;
        T[k].lazy=0;
    }
}
void build_tree(int l,int r,int k)
{
    T[k].lazy=0,T[k].val=0;
    if(l==r)
    {
        T[k].val=w[val[l]];
        return ;
    }
    int mid=(l+r)>>1;
    build_tree(l,mid,k<<1);
    build_tree(mid+1,r,k<<1|1);
}
void update(int l,int r,int ql,int qr,int k,int val)
{
    if(l==ql&&r==qr)
    {
        T[k].val+=val;
        T[k].lazy+=val;
        return ;
    }
    push_down(k);
    int mid=(l+r)>>1;
    if(qr<=mid)
        update(l,mid,ql,qr,k<<1,val);
    else if(ql>mid)
        update(mid+1,r,ql,qr,k<<1|1,val);
    else
        update(l,mid,ql,mid,k<<1,val),update(mid+1,r,mid+1,qr,k<<1|1,val);
}
int query(int l,int r,int k,int id)
{
    if(l==r)
        return T[k].val;
    push_down(k);
    int mid=(l+r)>>1;
    if(id<=mid) return query(l,mid,k<<1,id);
    else return query(mid+1,r,k<<1|1,id);
}
void chang(int x,int y,int val)//修改x到y路径节点上的值
{
    while(top[x]!=top[y])//x和y不在同一条重链上
    {
        if(dep[top[x]]<dep[top[y]]) swap(x,y);//将深度深的节点道这条重链顶端的区间更新
        update(1,n,id[top[x]],id[x],1,val);
        x=fa[top[x]];
    }
    if(dep[x]>dep[y]) swap(x,y);
    update(1,n,id[x],id[y],1,val);//当在同一条重链上时更新x到y之间的这段区间就好了
}
int main()
{
    while(~scanf("%d%d%d",&n,&m,&q))
    {
        init();
        for(int i=1;i<=n;i++)
            scanf("%d",&w[i]);
            for(int i=0;i<m;i++)
            {
                int a,b;
                scanf("%d%d",&a,&b);
                add(a,b);
                add(b,a);
            }
            dfs1(1,0,0);//剖树链
            dfs2(1,1);//剖树链
            build_tree(1,n,1);//建立线段树
            while(q--)//根据线段树求解
            {
                char c;
                scanf(" %c",&c);

                if(c=='Q')
                {
                    int a;
                    scanf("%d",&a);
                    printf("%d\n",query(1,n,1,id[a]));
                }
                else
                {
                    int a,b,v;
                    scanf("%d%d%d",&a,&b,&v);
                    if(c=='D') v=-v;
                    chang(a,b,v);
                }
            }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值