洛谷-3178 [HAOI2015]树上操作

本文深入探讨了树链剖分算法,一种高效处理树形结构数据查询与更新的算法。通过将树分解成一系列链,可以快速进行点更新、子树更新及路径查询等操作。文章详细介绍了算法原理、数据结构设计与实现代码,适用于处理大规模数据集上的复杂树操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述
有一棵点数为 N 的树,以点 1 为根,且树点有边权。然后有 M 个操作,分为三种:
操作 1 :把某个节点 x 的点权增加 a 。
操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。
操作 3 :询问某个节点 x 到根的路径中所有点的点权和。
输入格式
第一行包含两个整数 N, M 。表示点数和操作数。
接下来一行 N 个整数,表示树中节点的初始权值。
接下来 N-1 行每行两个正整数 from, to , 表示该树中存在一条边 (from, to) 。
再接下来 M 行,每行分别表示一次操作。其中第一个数表示该操作的种类( 1-3 ) ,之后接这个操作的参数( x 或者 x a ) 。
输出格式
对于每个询问操作,输出该询问的答案。答案之间用换行隔开。

输入输出样例
输入 #1
5 5
1 2 3 4 5
1 2
1 4
2 3
2 5
3 3
1 2 1
3 5
2 1 2
3 3

输出 #1
6
9
13

说明/提示
对于 100% 的数据, N,M<=100000 ,且所有输入数据的绝对值都不会超过 10610^6106

解释:树链剖分题,查找以X为根的子树,其实就是查找[dfn[x],dfn[x]+sz[x]−1].[dfn[x],dfn[x]+sz[x]-1].[dfn[x],dfn[x]+sz[x]1].,其中dfn[x]dfn[x]dfn[x]dfsdfsdfs序,sz[x]sz[x]sz[x]表示以X为根的节点个数。其他都是裸模板


#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
inline char nc() {
    static char buf[2000000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,2000000,stdin),p1==p2)?EOF:*p1++;
}
template <class Tp> inline void read(register Tp &s) {
    s=0;
    register bool neg=0;
    register char c=nc();
    for(;c<'0'||c>'9';c=nc()) neg|=(c=='-');
    for(;c>='0'&&c<='9';s=s*10+(c^48),c=nc());
    s=(neg?-s:s);
}

const ll N=200000,M=200000;
ll n,idx,dfn[N],seq[N],U[N],V[N],W[N],val[N],fa[N],dep[N],top[N],sz[N],hvy[N];
ll lazy[N]={0};
ll tot,lnk[N],ter[M],nxt[M],seg[N<<2];

void add(ll u,ll v) {
    ter[++tot]=v,nxt[tot]=lnk[u],lnk[u]=tot;
}
void dfs1(ll u,ll f) {
    fa[u]=f,dep[u]=dep[f]+1,sz[u]=1,hvy[u]=top[u]=0;
    for(ll i=lnk[u];i;i=nxt[i]) {
        ll v=ter[i];
        if(v==f) continue;
        dfs1(v,u);
        sz[u]+=sz[v];
        if(sz[hvy[u]]<sz[v]) hvy[u]=v;
    }
}
void dfs2(ll u,ll tp){
    dfn[u]=++idx,seq[idx]=u,top[u]=tp;
    if(!hvy[u]) return;
    dfs2(hvy[u],tp);
    for(ll i=lnk[u];i;i=nxt[i]) {
        ll v=ter[i];
        if(v==fa[u]||v==hvy[u]) continue;
        dfs2(v,v);
    }
}
void pushup(ll rt) {
    seg[rt]=seg[lson]+seg[rson];
}
void pushdown(ll rt,ll l,ll r){
    ll mid=(l+r)/2;
    seg[lson]+=lazy[rt]*(mid-l+1);
    seg[rson]+=lazy[rt]*(r-mid);
    lazy[lson]+=lazy[rt];
    lazy[rson]+=lazy[rt];
    lazy[rt]=0;
}
void build(ll rt,ll l,ll r) {
    if(l==r) {
        seg[rt]=val[seq[l]];
        return;
    }
    ll mid=(l+r)>>1;
    build(lson,l,mid);
    build(rson,mid+1,r);
    pushup(rt);
}
void modify(ll l,ll r,ll rt,ll L,ll R,ll val) {
    pushdown(rt,L,R);
    if(l<=L&&R<=r){
        seg[rt]+=val*(R-L+1);
        lazy[rt]+=val;
        return;
    }
    ll mid=(L+R)>>1;
    if(l<=mid) modify(l,r,lson,L,mid,val);
    if(r>mid) modify(l,r,rson,mid+1,R,val);
    pushup(rt);
}
ll query(ll x,ll y,ll rt,ll l,ll r){
    pushdown(rt,l,r);
    if(x>y) return 0;
    if(x<=l&&r<=y) return seg[rt];
    ll mid=(l+r)>>1,res=0;
    if(x<=mid) res+=query(x,y,lson,l,mid);
    if(mid<y) res+=query(x,y,rson,mid+1,r);
    return res;
}
ll chainQuery(ll u,ll v){
    ll res=0;
    for(ll fu=top[u],fv=top[v];fu^fv;u=fa[fu],fu=top[u]){
        if(dep[fu]<dep[fv]) swap(u,v),swap(fu,fv);
        res+=query(dfn[fu],dfn[u],1,1,n);
    }
    if(dep[u]>dep[v]) swap(u,v);
    res+=query(dfn[u],dfn[v],1,1,n);
    return res;
}ll m=0;
int main(){
    scanf("%lld%lld",&n,&m);
    for(ll i=1;i<=n;i++) scanf("%lld",&val[i]);
    for(ll i=1;i<n;++i){
        ll u,v;
        scanf("%lld%lld",&u,&v);
        add(u,v),add(v,u);
    }
    dfs1(1,0),dfs2(1,1);
    build(1,1,n);
    while(m--){
        ll cmd;scanf("%lld",&cmd);
        if(cmd==1){
            ll x,a;scanf("%lld%lld",&x,&a);
            modify(dfn[x],dfn[x],1,1,n,a);
        }else if(cmd==2){
            ll x,a;scanf("%lld%lld",&x,&a);
            modify(dfn[x],dfn[x]+sz[x]-1,1,1,n,a);
        }else{
            ll x=0;scanf("%lld",&x);
            printf("%lld\n",chainQuery(1,x));
        }
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值