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
",query_path(u,u));
            }
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/chanme/p/3415673.html