HDU 3966 Aragorn's Story 树链剖分

    第一道树链剖分,学习的时候参考了下面连接博主的文章,含义深入浅出,看了之后就非常清楚树链剖分的原理了:http://blog.sina.com.cn/s/blog_7a1746820100wp67.html

    个人的理解是,树链剖分实际上是在树于链的结构中找到了一种平衡,一个单纯的链在树链剖分的角度来看是一条长长的重链,在处理的时候等效于平常的区间线段树,但是当变成树路径的时候,边不具有连续性,因此很难去用线段树去维护,所以自然的,有人想出了一个办法,通过某种办法对树上的链进行编号,使得两个点的路径上的边尽可能多的连续起来,而且连续的和不连续的边加起来不超过logn条,这样我在更新线段树的时候只需要找出logn条这些边,每次logn的更新,所以每次修改树路径就是log^2n的代价.

    用术语来说,重链就是连续的边,轻边就是不连续的.所以树链剖分实际上是利用logn的代价将树路径转换成了区间.所以对树路径的所有操作都转化成最基本的线段树的问题.你所要做的就是做两次dfs,分好轻重链,两边不断往上更新到lca处.链接中的博客给了一个很好的范本.

    题目给的是边权,点权也是同样可以处理的,将点权放在连向父亲的那条边上(对于非根结点).对于根结点,我们可以再弄一个根的根结点(下面代码里就是0号)连一条边,最后每个点权都会对应于一条边.其中相应的代码中有些细节也是需要修改的.不过自己写的跑的微慢呀...用了2s..

#pragma comment(linker, "/STACK:102400000,102400000")

#include<iostream>

#include<cstdio>

#include<cstring>

#include<string>

#include<algorithm>

#include<vector>

#include<cmath>

#define maxn 50000

#define ll long long

using namespace std;



vector<int> G[maxn+50];

int n,m,p;

int son[maxn+50],w[maxn+50],fa[maxn+50],siz[maxn+50],top[maxn+50],dep[maxn+50],totw;

int edge[maxn+50];

int wht[maxn+50];



struct Node

{

    int l,r;

    ll sum,add;

}N[4*maxn+50];



void pushUp(int i)

{

    N[i].sum=N[i<<1].sum+N[i<<1|1].sum;

}

void pushDown(int i)

{

    if(N[i].add!=0){

        if(N[i].l!=N[i].r){

            int t=N[i].add;

            N[i<<1].sum+=(N[i<<1].r-N[i<<1].l+1)*t;

            N[i<<1|1].sum+=(N[i<<1|1].r-N[i<<1|1].l+1)*t;

            N[i<<1].add+=t;

            N[i<<1|1].add+=t;

        }

        N[i].add=0;

    }

}



void build(int i,int L,int R)

{

    N[i].l=L;N[i].r=R;N[i].add=0;

    if(L==R){

        N[i].sum=edge[L];

        return;

    }

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

    build(i<<1,L,M);

    build(i<<1|1,M+1,R);

    pushUp(i);

}



void add(int i,int L,int R,int val)

{

    if(N[i].l==L&&N[i].r==R){

        N[i].sum+=(R-L+1)*val;

        N[i].add+=val;

        return;

    }

    pushDown(i);

    int M=(N[i].l+N[i].r)>>1;

    if(M>=R){

        add(i<<1,L,R,val);

    }

    else if(M<L){

        add(i<<1|1,L,R,val);

    }

    else{

        add(i<<1,L,M,val);

        add(i<<1|1,M+1,R,val);

    }

    pushUp(i);

}



int query(int i,int L,int R)

{

    if(N[i].l==L&&N[i].r==R){

        return N[i].sum;

    }

    pushDown(i);

    int M=(N[i].l+N[i].r)>>1;

    if(M>=R){

        return query(i<<1,L,R);

    }

    else if(M<=L){

        return query(i<<1|1,L,R);

    }

    else{

        return query(i<<1,L,M)+query(i<<1|1,M+1,R);

    }

    pushUp(i);

}



void dfs1(int v)

{

    siz[v]=1;son[v]=0;int msize=0;

    for(int i=0;i<G[v].size();++i){

        int u=G[v][i];

        if(u==fa[v]) continue;

        fa[u]=v;

        dep[u]=dep[v]+1;

        dfs1(u);

        if(siz[u]>msize) {son[v]=u;msize=siz[u];}

        siz[v]+=siz[u];

    }

}



void dfs2(int v,int tp)

{

    w[v]=++totw;top[v]=tp;edge[w[v]]=wht[v];

    if(son[v]!=0) dfs2(son[v],top[v]);

    for(int i=0;i<G[v].size();++i){

        int u=G[v][i];

        if(u!=fa[v]&&u!=son[v]){

            dfs2(u,u);

        }

    }

}



void initEdge()

{

    fa[0]=totw=dep[0]=0;

    dfs1(0);dfs2(0,0);

    build(1,1,n+1);

}



ll query_path(int va,int vb)

{

    ll res=0;

    int f1=top[va],f2=top[vb];

    while(f1!=f2)

    {

        if(dep[f1]<dep[f2]){

            swap(f1,f2);swap(va,vb);

        }

        res+=(ll)query(1,w[f1],w[va]);

        va=fa[f1];f1=top[va];

    }

    if(va==vb) return res+query(1,w[va],w[vb]);

    if(dep[va]>dep[vb]) swap(va,vb);

    res+=(ll)query(1,w[va],w[vb]);

    return res;

}



void add_path(int va,int vb,int val)

{

    int f1=top[va],f2=top[vb];

    while(f1!=f2)

    {

        if(dep[f1]<dep[f2]){

            swap(f1,f2);swap(va,vb);

        }

        add(1,w[f1],w[va],val);

        va=fa[f1];f1=top[va];

    }

    if(va==vb) {add(1,w[va],w[va],val);return;}

    if(dep[va]>dep[vb]) swap(va,vb);

    add(1,w[va],w[vb],val);

}



int main()

{

    while(cin>>n>>m>>p)

    {

        for(int i=1;i<=n;++i){

            scanf("%d",&wht[i]);

            G[i].clear();

        }

        int u,v;

        for(int i=0;i<n-1;++i){

            scanf("%d%d",&u,&v);

            G[u].push_back(v);G[v].push_back(u);

        }

        G[0].push_back(1);

        initEdge();

        char s[3];int val;

        for(int i=0;i<p;++i){

            scanf("%s",&s);

            if(s[0]=='I'||s[0]=='D'){

                scanf("%d%d%d",&u,&v,&val);

                if(s[0]=='I'){

                    add_path(u,v,val);

                }

                else{

                    add_path(u,v,-val);

                }

            }

            else{

                scanf("%d",&u);

                printf("%d\n",query_path(u,u));

            }

        }

    }

    return 0;

}

 

你可能感兴趣的:(HDU)