初涉树链剖分 hrbust 2065

一般适用于对于树的区间查询与修改。

几个定义:

树链:树上两点之间的路径。

剖分:将树上的边划分为轻边和重边。

重儿子:在 u 的儿子节点中,siz最大的那个节点即为 u 的重儿子。(若存在多个,则任选一个)

轻儿子:除重儿子之外的所有儿子节点均为轻儿子。

重边:父节点与重儿子之间的连边。

轻边:父节点与轻儿子之间的连边。

重链:由重边首尾相连组成的路径。


操作之前需要通过dfs初始化一些信息,fa[] , son[] , siz[] , top[] , dot_site[] , dep[] ,seg[]。 

fa[s] :s 的父节点。根节点无意义。

son[s] :s的重儿子。叶子节点无意义。

dep[s] :s的深度。

top[s] :s所在链的深度最小的点。

siz[s] :以s为根节点的子树上的节点总数。

dot_site[s] , s 在dfs时的时间戳。

seg为dfs时的次序。



如图所示:

途中粗线即为重边,细线即为轻边。

则图中重链有 :1. (1 -> 4 -> 7 -> 10) 2. (2 -> 12)  3.  ( 6->9 ) 。

图中点旁边的数字即为相应的点的 top[] 。

为了保证重链上的点在 dfs 时连续,则在dfs时要优先对其重儿子进行dfs(显然除叶子节点外的所有节点均存在重儿子)。

则dfs完成后 seg存储的内容为 { 1,4,7,10,11,6,9,5,2,12 , 3}。


然后对 seg 建立线段树,继而对线段树进行查询和更新。

因为任意两点间的重链和轻边的数量均不超过 log(n),n 两点之间的边的条数,所以在对任意一段路径查询和更新时,其对线段

树的操作次数不会超过2*log(n)。(所示树链剖分的一个性质吧)。


以查询为例。

while(1)

{

设 fu = top[u] , fv = top[v]。

1. 当fu != fv(设 dep[fu] > dep[fv] ) 时,查询seg[] 的子区间 [ dot_site[fu] , dot_site[u] ]上的最值  , u = fa[fu] .

2. 当 fu == fv 时,u,v在一条重链上,若 u == v,查询结束,否则查询 [dot_site[ u ] ,dot_site[ v ] ] (设dep[u] < dep[v]),查询结束。

}


心情有点小低谷。。。墨迹了好久才写完。。。离别的前奏总是这样伤感。


___________ Updata 3.28_____________


总算有一份还算满意的代码了。。

#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <queue>
#include <cmath>
#include <stack>

#pragma comment(linker, "/STACK:1024000000");
#define EPS (1e-6)
#define LL long long
#define ULL unsigned long long int
#define _LL __int64
#define _INF 0x3f3f3f3f
#define Mod 1000000007

using namespace std;

const int MAXN = 30010;

struct N
{
    int u,v,next;
}edge[MAXN*2];

int head[MAXN];

int Top;

void Link(int u,int v)
{
    edge[Top].u = u , edge[Top].v = v , edge[Top].next = head[u] , head[u] = Top++;
}

int Top_E;

int siz[MAXN],fa[MAXN],son[MAXN],dot_site[MAXN],dep[MAXN],top[MAXN],val[MAXN],seg[MAXN];

void dfs1(int s,int pre,int d)
{
    dep[s] = d , fa[s] = pre , siz[s] = 1 , son[s] = -1;

    for(int p = head[s] ; p != -1; p = edge[p].next)
    {
        if(edge[p].v != pre)
        {
            dfs1(edge[p].v,s,d+1);

            if(son[s] == -1 || siz[son[s]] < siz[edge[p].v])
                son[s] = edge[p].v;

            siz[s] += siz[edge[p].v] + 1;
        }
    }
}

void dfs2(int s,int pre,int T)
{
    top[s] = T , dot_site[s] = ++Top_E , seg[Top_E] = s ;

    if(son[s] == -1)
        return ;

    dfs2(son[s],s,T);

    for(int p = head[s] ; p != -1; p = edge[p].next)
    {
        if(edge[p].v != son[s] && edge[p].v != pre)
        {
            dfs2(edge[p].v,s,edge[p].v);
        }
    }
}

struct ST
{
    int Max,Sum;
}st[MAXN*4];

void Init(int site, int l , int r)
{
    if(l == r)
    {
        st[site].Max = val[seg[l]] , st[site].Sum = val[seg[r]];
        return ;
    }

    int mid = (l+r)>>1;

    Init(site<<1,l,mid);
    Init(site<<1|1,mid+1,r);

    st[site].Sum = st[site<<1].Sum + st[site<<1|1].Sum;
    st[site].Max = max(st[site<<1].Max , st[site<<1|1].Max);
}

void Updata(int site,int l,int r,int x)
{
    if(l == r && r == x)
    {
        st[site].Max = val[seg[l]] , st[site].Sum = val[seg[r]];
        return ;
    }

    int mid = (l+r)>>1;

    if(x <= mid)
        Updata(site<<1,l,mid,x);
    else
        Updata(site<<1|1,mid+1,r,x);

    st[site].Sum = st[site<<1].Sum + st[site<<1|1].Sum;
    st[site].Max = max(st[site<<1].Max , st[site<<1|1].Max);
}

ST Query(int site,int L,int R,int l,int r)
{
    if(L == l && R == r)
    {
        return st[site];
    }

    int mid = (L+R)>>1;

    if(r <= mid)
        return Query(site<<1,L,mid,l,r);
    else if(mid < l)
        return Query(site<<1|1,mid+1,R,l,r);

    ST t1,t2,t3;

    t1 = Query(site<<1,L,mid,l,mid);
    t2 = Query(site<<1|1,mid+1,R,mid+1,r);

    t3.Max = max(t1.Max,t2.Max);
    t3.Sum = t1.Sum + t2.Sum;
    return t3;
}

int main()
{
    //freopen("C.in","r",stdin);
    //freopen("C.out","w",stdout);

    char s[20];

    int n,i;

    int u,v;

    while(scanf("%d",&n) != EOF)
    {
        memset(head,-1,sizeof(int)*(n+2));
        Top = 0;

        for( i= 1;i < n; ++i)
        {
            scanf("%d %d",&u,&v);
            Link(u,v);
            Link(v,u);
        }

        for(i = 1;i <= n; ++i)
        {
            scanf("%d",&val[i]);
        }

        dfs1(1,-1,1);

        Top_E = 0;

        dfs2(1,-1,1);

        Init(1,1,Top_E);

        int q;

        scanf("%d",&q);

        while(q--)
        {
            scanf("%*c%s %d %d",s,&u,&v);

            if(s[1] == 'H')
            {
                val[u] = v;
                Updata(1,1,Top_E,dot_site[u]);
            }
            else
            {
                ST anw,temp;
                anw.Sum = 0;
                anw.Max = val[u];

                int fu = top[u],fv = top[v];

                while(fu != fv)
                {
                    if(dep[fu] < dep[fv])
                        swap(u,v),swap(fu,fv);

                    temp = Query(1,1,Top_E,dot_site[fu],dot_site[u]);

                    anw.Sum += temp.Sum;
                    anw.Max = max(anw.Max,temp.Max);

                    u = fa[fu];

                    fu = top[u];
                }

                if(dep[u] < dep[v])
                    swap(u,v);

                temp = Query(1,1,Top_E,dot_site[v],dot_site[u]);

                anw.Sum += temp.Sum;
                anw.Max = max(anw.Max,temp.Max);

                if(s[1] == 'M')
                    printf("%d\n",anw.Max);
                else if(s[1] == 'S')
                    printf("%d\n",anw.Sum);

            }
        }
    }
    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值