题解 P3833 【[SHOI2012]魔法树】

· · 题解

如果你还不会树链剖分,可以先尝试这道模板题:【模板】轻重链剖分

这道题可以说是树链剖分套线段树的模板题,蒟蒻的我花了一下午时间来练树链剖分后终于好像搞懂了呢!虽然看到其他题解有用LCT做,但是蒟蒻的我表示我并不会。

首先介绍一下树链剖分:

根据树的概念,树上存在着许多的链,如本图中,就含有很多的链,例如3-2-1-9-10-11就是其中一条链。

对于一棵树,如果我们已经找出了一条链的情况下,它在链上的操作是线性的。此外,一条链周围点,也可以通过这条链实现快速的维护。

例如,还是上图中的3-2-1-9-10-11这一条链,当我们找出它时,便可以快速的维护4这个点(指4向上一步就可以进入链中,复杂度几乎属于常数),减少了树型结构的复杂度。

综上可知,链在树型结构中的地位非常重要,一颗树可以根据不同剖分成多条链,从而将树型结构优化成接近线性结构,进而运用数据结构维护。

树链剖分分为:重链剖分、长链剖分、实链剖分,一般说树链剖分指的是重链剖分。

我们给出一些定义,并按以下规则划分一颗树:

定义:

重子节点——表示其子节点中子树最大的子结点。如果有多个子树最大的子结点,取其一。如果没有子节点,就无重子节点。

轻子节点——表示剩余的所有子结点。

从这个结点到重子节点的边为重边。

到其他轻子节点的边为轻边。

若干条首尾衔接的重边构成重链。

把落单的结点也当作重链,那么整棵树就被剖分成若干条重链。

如图所示,当前一颗树已经被按上述要求分成了若干条链。

代码实现:两次dfs

第一次dfs:

fa:记录父节点

dep:记录当前深度

size:记录子树规模

son:当前重儿子是谁,这里主要处理出了每个节点的子树规模以及找出了重儿子,为接下来剖分做准备

void dfs1(int x,int Fa)
{
    fa[x]=Fa;
    size[x]=1;
    dep[x]=dep[Fa]+1;
    for(int i=head[x];i;i=nxt[i])
    if(to[i]!=fa[x])
    {
        dfs1(to[i]);
        size[x]+=size[to[i]];
        if(size[son[x]]<size[to[i]])son[x]=to[i];
    }
}

第二次dfs:

top:当前每个点所在链的顶端节点

seg、rev:对每一个节点按 按链的顺序重新编号,这样同一链上的点编号相邻

这里找出了每个点对应的链的顶点,以此划分链

void dfs2(int x)
{
    if(son[fa[x]]!=x)top[x]=x;
    else top[x]=top[fa[x]];
    seg[x]=++dfn;
    rev[dfn]=x;
    if(son[x])dfs2(son[x]);
    for(rg i=head[x];i;i=nxt[i])
    if(to[i]!=fa[x]&&to[i]!=son[x])
    dfs2(to[i]);
}

重链剖分的性质:

树上每个节点都属于且仅属于一条重链 。

所有的重链将整棵树 完全剖分 。

在剖分时优先遍历重儿子,最后重链的 DFS 序就会是连续的。

一颗子树内的 DFN 序是连续的。

可以发现,当我们向下经过一条 轻边 时,所在子树的大小至少会除以二因此每过一条链复杂度至少除以二,最终复杂度为logn级别。

因此,对于树上的任意一条路径,把它拆分成从lca分别向两边往下走,分别最多走logn次,因此,树上的每条路径都可以被拆分成不超过logn条重链。

<此处介绍大多引用xj学长的细心讲解啦~~~>

然后我们就可以干很多事情,例如

套线段树!!

因此我们就可以把线段树的板子打上去了!!

这道题就用线段树维护区间和以及区间最大值,但因为树链剖分后每次change_son修改的的区间是从seg[ x ]到seg[ x ]+size[ x ]-1(相当于seg记录的是dfs序啦)

(并且这道题要记得开long long呦)

Code:

< 提供自认为比较清新的码风 >

#define Flowery
#define maxn 400000
#define ll long long
#define rg register long long
#include<bits/stdc++.h>
using namespace std;
int T,n;
struct node
{
    ll l,r,val,maxx,lazy;
}gy[maxn];
int to[maxn],head[maxn],nxt[maxn],cnt;
inline void add(ll x,ll y)
{
    to[++cnt]=y;
    nxt[cnt]=head[x];
    head[x]=cnt;
}
int fa[maxn],dep[maxn],size[maxn],son[maxn];
void dfs1(ll x)
{
    size[x]=1;
    dep[x]=dep[fa[x]]+1;
    for(rg i=head[x];i;i=nxt[i])
    if(to[i]!=fa[x])
    {
        dfs1(to[i]);
        size[x]+=size[to[i]];
        if(size[son[x]]<size[to[i]])son[x]=to[i];
    }
}
int top[maxn],seg[maxn],rev[maxn],dfn;
void dfs2(ll x)
{
    if(son[fa[x]]!=x)top[x]=x;
    else top[x]=top[fa[x]];
    seg[x]=++dfn;
    rev[dfn]=x;
    if(son[x])dfs2(son[x]);
    for(rg i=head[x];i;i=nxt[i])
    if(to[i]!=fa[x]&&to[i]!=son[x])dfs2(to[i]);
}
void push_up(ll x)
{
    gy[x].val=gy[2*x].val+gy[2*x+1].val;
}
void push_down(ll x)
{
    if(gy[x].lazy)
    {
        gy[2*x].lazy+=gy[x].lazy;
        gy[2*x+1].lazy+=gy[x].lazy;
        gy[2*x].val+=(gy[2*x].r-gy[2*x].l+1)*gy[x].lazy;
        gy[2*x+1].val+=(gy[2*x+1].r-gy[2*x+1].l+1)*gy[x].lazy;
        gy[x].lazy=0;
    }
}
void build(ll x,ll l,ll r)
{
    gy[x].l=l;gy[x].r=r;
    if(l==r)return;
    ll mid=(l+r)>>1;
    build(2*x,l,mid);
    build(2*x+1,mid+1,r);
    push_up(x);
}
void modify(ll x,ll l,ll r,ll v)
{
    if(gy[x].l>r||gy[x].r<l)return;
    if(l<=gy[x].l&&gy[x].r<=r)
    {
        gy[x].lazy+=v;
        gy[x].val+=(gy[x].r-gy[x].l+1)*v;
        return;
    }
    push_down(x);
    modify(2*x,l,r,v);
    modify(2*x+1,l,r,v);
    push_up(x);
}
ll query(ll x,ll l,ll r)
{
    if(gy[x].l>r||gy[x].r<l)return 0;
    if(l<=gy[x].l&&gy[x].r<=r)return gy[x].val;
    push_down(x);
    return query(2*x,l,r)+query(2*x+1,l,r);
}
void change(ll u,ll v,ll val)
{
    while(top[u]!=top[v])
    {
        if(dep[top[u]]<dep[top[v]])swap(u,v);
        modify(1,seg[top[u]],seg[u],val);
        u=fa[top[u]];
    }
    if(dep[u]<dep[v])swap(u,v);
    modify(1,seg[v],seg[u],val);
}
ll u,v,d;
char opt[2];
int main()
{
    scanf("%lld",&n);fa[1]=-1;
    for(rg i=1;i<n;i++)
    {
        scanf("%lld%lld",&u,&v);u++;v++;
        add(u,v);add(v,u);
        fa[v]=u;
    }
    dep[1]=1;dfs1(1);dfs2(1);build(1,1,n);
    scanf("%lld",&T);
    while(T--)
    {
        scanf("%s",opt);
        if(opt[0]=='A')
        {
            scanf("%lld%lld%lld",&u,&v,&d);u++;v++;
            change(u,v,d);
        }
        if(opt[0]=='Q')
        {
            scanf("%lld",&u);u++;
            printf("%lld\n",query(1,seg[u],seg[u]+size[u]-1));
        }
    }
    return 0;
}

(11-3 modify:图片不小心崩啦)

my blog