树链剖分入门 & 洛谷模版题代码

为什么需要树链剖分?

因为需要一种数据结构来加速树上带修改的区间求和(包括某条链的和以及某个子树的和)

树链剖分的原理?

把一棵n个节点的树分成至多log2n条链,使得对于任意两个节点所连成的链(设这条链上有x个节点),所经过的【划分的链】的条数都不超过log2x。

同时,保证每一条链的编号是连续的,这样就可以把树上的操作转换为对于某个区间的操作,从而应用线段树进行求和(或其他操作)。使用的方法是dfs序。而dfs时先选取子节点个树最多的儿子【重儿子】,保证了每一条链尽可能长。这样求一条链的和只需要对于两个节点共同向上跳直到它们到达同一条链上即可。注意这里是链头低的跳而不是节点本身深度高的先跳(不然可能会跳过头)

具体实现?

先用一个dfs求出每个节点【子树中节点的个数】以及【重儿子】,再用一个dfs求出每个节点的dfs序,为它们加上新的编号(也就是进行线段树操作时所使用的编号),此时最先访问的是重儿子,其他的孩子顺序无所谓。

求链的和/链修改 类似于求两个点【各自所在的链】的一个【最近公共祖先链】(我瞎扯的名字),每次都进行求和即可。求子树的和/子树修改 相当于直接对于作为子树的区间进行操作(dfs序中子树的序号是连续的,十分方便)

代码

#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
#define lch o<<1
#define rch o<<1|1
#define mid (l+r>>1)
const int maxn = 1000005;

struct Edge
{
    ll v,next;    
}e[200010];

ll n,m,r,p,cnt,tot;
ll a[maxn],sumv[maxn<<3],addv[maxn<<3];
ll front[maxn],real[maxn],id[maxn],f[maxn];
ll hson[maxn],dep[maxn],siz[maxn],top[maxn];

void AddEdge(ll u,ll v)
{
    e[++cnt].v = v;
    e[cnt].next = front[u];
    front[u] = cnt;
}

void pushup(ll o)
{
    sumv[o] = sumv[lch] + sumv[rch];
}

void pushdown(ll o, ll l, ll r)
{
    if(addv[o])
    {
        int x = addv[o];
        addv[o] = 0;
        addv[lch] += x;
        addv[rch] += x;
        sumv[lch] += (mid - l + 1) * x;
        sumv[rch] += (r - mid) * x;
    }    
}

void build(ll o, ll l, ll r)
{
    if(l == r)
    {
        sumv[o] = a[real[l]];
        return;
    }    
    build(lch, l, mid);
    build(rch, mid+1, r);
    pushup(o);
}

void optadd(ll o, ll l, ll r, ll ql, ll qr, ll x)
{
    if(ql <= l && r <= qr)
    {
        sumv[o] += (r-l+1)*x;
        addv[o] += x;
        return;
    }
    pushdown(o, l, r);
    if(ql <= mid) optadd(lch,l,mid,ql,qr,x);
    if(mid+1 <= qr) optadd(rch,mid+1,r,ql,qr,x);
    pushup(o);
}

ll querysum(ll o, ll l, ll r, ll ql, ll qr)
{
    if(ql <= l && r <= qr)
    {
        return sumv[o] % p;
    }
    ll res = 0;
    pushdown(o, l, r);
    if(ql <= mid) (res += querysum(lch,l,mid,ql,qr)) %= p;
    if(mid+1 <= qr) (res += querysum(rch,mid+1,r,ql,qr)) %= p;
    pushup(o);
    return res;
}

void dfs1(ll u)
{
    dep[u] = dep[f[u]] + 1;
    siz[u] = 1;
    for (ll i = front[u]; i; i = e[i].next)
    {
        if(e[i].v != f[u])
        {
            f[e[i].v] = u;
            dfs1(e[i].v);
            siz[u] += siz[e[i].v];
            if(!hson[u] || siz[hson[u]] < siz[e[i].v]) 
            {
                hson[u] = e[i].v;
            }    
        }
    }
}

void dfs2(ll u)
{
    if(!top[u]) top[u] = u;
    id[u] = ++tot;
    real[tot] = u;
    if(hson[u])
    {
        top[hson[u]] = top[u];
        dfs2(hson[u]);
    }    
    for (ll i = front[u]; i; i = e[i].next)
    {
        if(e[i].v != f[u] && e[i].v != hson[u])
        {
            dfs2(e[i].v);
        }
    }
}

void chain_add()
{
    ll u,v,w;
    scanf("%lld%lld%lld",&u,&v,&w);
    ll tu = top[u], tv = top[v];
    while(tu != tv)
    {
        if(dep[tu] < dep[tv])
        {
            swap(u,v);
            swap(tu,tv);
        }
        optadd(1,1,n,id[tu],id[u],w);
        u = f[tu];
        tu = top[u];
    }
    if(dep[u] < dep[v]) swap(u,v);
    optadd(1,1,n,id[v],id[u],w);
}

void chain_query()
{
    ll u,v,res = 0;
    scanf("%lld%lld",&u,&v);
    ll tu = top[u], tv = top[v];
    while(tu != tv)
    {
        if(dep[tu] < dep[tv])
        {
            swap(u,v);
            swap(tu,tv);
        }
        res += querysum(1,1,n,id[tu],id[u]);
        res %= p;
        u = f[tu];
        tu = top[u];
    }    
    if(dep[u] < dep[v]) swap(u,v);
    res += querysum(1,1,n,id[v],id[u]);
    res %= p;
    printf("%lld\n",res);
}

void tree_add()
{
    ll u,w;
    scanf("%lld%lld",&u,&w);
    optadd(1,1,n,id[u],id[u] + siz[u] - 1, w);
}

void tree_query()
{
    ll u;
    scanf("%lld",&u);    
    ll res = querysum(1,1,n,id[u],id[u] + siz[u] - 1);
    res %= p;
    printf("%lld\n",res);
}
int main()
{
    scanf("%lld%lld%lld%lld",&n,&m,&r,&p);
    for (ll i = 1; i <= n; i++) scanf("%lld",&a[i]);
    for (ll i = 1; i < n; i++) 
    {
        ll u,v;
        scanf("%lld%lld",&u,&v);
        AddEdge(u,v);
        AddEdge(v,u);
    }    
    dep[r] = 1;
    dfs1(r);
    dfs2(r);
    build(1,1,n);
    for (ll i = 1; i <= m; i++)
    {
        int op;
        scanf("%d",&op);
        if(op == 1)
        {
            chain_add();
        }
        else if(op == 2)
        {
            chain_query();
        }
        else if(op == 3)
        {
            tree_add();
        }
        else if(op == 4)
        {
            tree_query();
        }
    }
    return 0;
}
View Code

(立个flag每个星期至少写三篇blog)

喜欢的男孩子的生日纪念

posted @ 2018-12-07 23:42  ELU_FOREVER  阅读(168)  评论(0编辑  收藏  举报