树链剖分入门-Hdu3966 Aragorn's Story

AC通道:http://acm.hdu.edu.cn/showproblem.php?pid=3966

[题目大意]

  一棵树上每个点有权值,每次支持三种操作:给[a,b]路径上的所有节点的权值加上k,给[a,b]路径上的所有节点的权值减去k,以及询问a的权值.

[分析]

  这是一道树链剖分模板题.

  树链剖分,就是将树化成了许多链,将这些链用数据结构保存起来,再去维护这个数据结构.

  假设给的树就是一条链,这道题当然很好办:直接将链用线段树存了,因为[a,b]的路径在线段树上也是连续的一段,那么修改一段就是log(n)的,询问节点也可以log(n)完成.

  但是给的是一棵树,树上两点间的路径虽然也是唯一确定的,但是在线段树上就不一定是连续的一段了.  

  于是乎,就需要一种将每条树上路径分成若干个连续段的算法.

  这就是树链剖分了.

  

  首先定义一下重儿子的概念:v是u的重儿子,当且仅当size(v)>=size(v‘)对于任意u的儿子v‘成立.就是u的儿子中所在子树最大的儿子.

  树链剖分将树分成重链和轻边,重链指的是每个节点与其重儿子相连,其重儿子再与其重孙子相连得到的链.轻边就是连接重链的边.

  [我这儿没图....要图找别人:http://blog.sina.com.cn/s/blog_7a1746820100wp67.html 这个图其实也长得不怎么好看]

  通过这样一个划分,就有了优美的性质:任意两点间的路径上最多有log(n)条重链.

  然后就可以将重链上的点连续地存进线段树中,每次将路径划分成若干个重链的部分来处理就可以了.

  这样下来每条路径处理就是log(n)*log(n)的.

  具体步骤:

    首先一遍dfs.处理出一些东西:每个节点子树的大小->每个点的重儿子,每个点的深度[这个在划分路径的时候有用],每个节点的父节点.

    然后再一遍dfs.给每个点标号,注意将重链的编号连续,同时记录每个点所在重链的顶端节点[这个怎么操作可以具体看代码]

    然后建线段树:跟往常一样的建立方法.

    

    询问:在线段树上询问,记得下传标记就好.

    修改:思考怎么划分,如果询问的两点,假设是x,y.

    当x,y处于同一条重链时:直接用线段树的区间修改,当不是同一条重链时,就需要将他们移动到同一条重链.

    首先判断x,y所在重链,谁的上端距离根更远,假设是x,那么将x调至其所在重链的上端的父亲节点[这样就移动到了另一条重链上],同时还要将x到重链上端的这一部分在线段树中加tag.

    依次这样操作,就可以等到x,y在同一条重链上的时候了.[具体也可以看代码]

  希望通过这道题,大家也就能大概理解树链剖分的思想所在了...

  献上代码:

#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

inline int in(){
    int x=0,f=1;char ch=getchar();
    while((ch>‘9‘ || ch<‘0‘) && ch!=‘-‘) ch=getchar();
    if(ch==‘-‘) f=-1,ch=getchar();
    while(ch>=‘0‘ && ch<=‘9‘) x=x*10+ch-‘0‘,ch=getchar();
    return x*f;
}

const int maxn=50010;

int n,m,q;
int cnt;
int a[maxn],id[maxn],Idex,rk[maxn];
int depth[maxn],sz[maxn],fa[maxn];
int top[maxn],son[maxn],head[maxn];
char ord[2];

struct Spot{
    int data,next;
}spot[maxn<<1];

#define now spot[pt].data
#define then spot[pt].next

struct Node{
    int dt,tg;
}s[maxn<<2];

inline void add(int u,int v){
    spot[cnt].data=v;spot[cnt].next=head[u];head[u]=cnt++;
    spot[cnt].data=u;spot[cnt].next=head[v];head[v]=cnt++;
}

void dfs1(int x,int f){
    sz[x]=1,fa[x]=f;
    for(int pt=head[x];pt!=-1;pt=then)
        if(now!=f){
            depth[now]=depth[x]+1;
            dfs1(now,x);
            sz[x]+=sz[now];
            if(son[x]<0 || sz[now]>sz[son[x]])
                son[x]=now;
        }
}

void dfs2(int x,int tp){
    top[x]=tp,id[x]=++Idex,rk[id[x]]=x;
    if(son[x]<0) return;
    dfs2(son[x],tp);
    for(int pt=head[x];pt!=-1;pt=then)
        if(now!=fa[x] && now!=son[x])
            dfs2(now,now);
}

void build(int l,int r,int rt){
    s[rt].dt=s[rt].tg=0;
    if(l==r){
        s[rt].dt=a[rk[l]];return;
    }
    int mid=(l+r)>>1;
    build(l,mid,rt<<1);
    build(mid+1,r,rt<<1|1);
}

inline void push_down(int x){
    if(s[x].tg){
        s[x<<1].tg+=s[x].tg,s[x<<1].dt+=s[x].tg;
        s[x<<1|1].tg+=s[x].tg,s[x<<1|1].dt+=s[x].tg;
        s[x].tg=0;
    }
}

void add_tag(int l,int r,int x,int y,int rt,int val){
    if(l==x && r==y){
        s[rt].dt+=val,s[rt].tg+=val;
        return;
    }
    int mid=(l+r)>>1;
    if(y<=mid) add_tag(l,mid,x,y,rt<<1,val);
    else if(x>mid) add_tag(mid+1,r,x,y,rt<<1|1,val);
    else add_tag(l,mid,x,mid,rt<<1,val),add_tag(mid+1,r,mid+1,y,rt<<1|1,val);
}

int query(int l,int r,int rt,int k){
    if(l==r)
        return s[rt].dt;
    push_down(rt);
    int mid=(l+r)>>1;
    if(k<=mid) return query(l,mid,rt<<1,k);
    else return query(mid+1,r,rt<<1|1,k);
}

void modify(int x,int y,int val){
    while(top[x]!=top[y]){
        if(depth[top[x]]<depth[top[y]]) swap(x,y);
        add_tag(1,n,id[top[x]],id[x],1,val);
        x=fa[top[x]];
    }
    if(id[x]>id[y]) swap(x,y);
    add_tag(1,n,id[x],id[y],1,val);
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("3966.in","r",stdin);
    freopen("3966.out","w",stdout);
#endif

    int u,v,x;

    while(~scanf("%d%d%d",&n,&m,&q)){
        Idex=cnt=0;
        for(int i=1;i<=n;i++)
            a[i]=in(),head[i]=son[i]=-1;
        for(int i=1;i<n;i++)
            u=in(),v=in(),add(u,v); 

        dfs1(1,0);
        dfs2(1,1);
        build(1,n,1);

        while(q--){
            scanf("%s",ord);
            if(ord[0]==‘Q‘){
                x=in();
                printf("%d\n",query(1,n,1,id[x]));
            }
            else{
                u=in(),v=in(),x=in();
                if(ord[0]==‘D‘) x=-x;
                modify(u,v,x);
            }
        }
    }
    return 0;
}

树链剖分入门-Hdu3966 Aragorn's Story

时间: 2024-08-08 10:54:35

树链剖分入门-Hdu3966 Aragorn's Story的相关文章

HDU - 3966 Aragorn&#39;s Story(树链剖分入门+线段树)

HDU - 3966 Aragorn's Story Time Limit: 3000MS   Memory Limit: 32768KB   64bit IO Format: %I64d & %I64u Submit Status Description Our protagonist is the handsome human prince Aragorn comes from The Lord of the Rings. One day Aragorn finds a lot of ene

树链剖分 [HDU 3966] Aragorn&#39;s Story

Aragorn's Story Time Limit: 10000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 3544    Accepted Submission(s): 995 Problem Description Our protagonist is the handsome human prince Aragorn comes from The Lord

洛谷 P2590 树的统计 P3178 树上操作【树链剖分入门】

题目描述 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w. 我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 III. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身 输入格式: 输入文件的第一行为一个整数n,表示节点的个数. 接下来n – 1行,每行2个整数a和b,表示节点a和节点b之间有一条边相连

树链剖分入门

这几天学了一个树链剖分,觉得还不是很难,这里我试着讲一讲吧. 首先,我认为树链剖分是把在树上一个节点一个节点的走改为按照某种规则跳,从而降低了时间复杂度. 那这是什么规则呢? 首先我们得知道什么是重链,知道什么是重链就得先知道什么是重儿子,重儿子就是子树较大的儿子.然后对于一个点,我们总是往他的重儿子走,这样就构成了重链,那么剩下的就是轻链. 放张图直观些 然后我们同样可以对树进行dfs,只不过重儿子优先,这样我们也得到了一个dfs序,于是我们把树上问题成功转化成了线性问题.接着就可以用线段树等

树链剖分 入门

什么是树链剖分? 树链剖分说白了就是将树的节点按照某种顺序编号,使其在特殊的链上编号连续(类似区间),方便用数据结构维护. 如何树链剖分? 树链剖分一般分为重链剖分和长链剖分,这里只介绍重链剖分(我也只会重链剖分). 重链剖分中有几个概念: 重儿子:一个节点的所有子节点中,以某个子节点为根的子树中节点数量最多的称为重儿子(如果最多的数量相同随便取一个). 轻儿子:一个节点的所有子节点中,不是重儿子的节点就是轻儿子(这不是废话吗). 重边:父节点与重儿子所连成的边. 轻边:父节点与轻儿子所连成的边

spoj375 Query on a tree(树链剖分入门题)

#include <iostream> #include <algorithm> #include <cstring> #include <cmath> #include <queue> #include <map> #include <set> #include <vector> #include <cstdio> using namespace std; const int MAXN=10010

Hdu3966( Aragorn&#39;s Story ) 树链剖分

点更新树链剖分入门题,诶 错了好多发,提到同一点时没更新,边数组开小了一倍. #include <cstdio> #include <algorithm> #include <iostream> #include <string.h> using namespace std; #define lson l,mid,rt<<1 #define rson mid+1,r,rt<<1|1 const int maxn = 55555; in

HDU3966 Aragorn&#39;s Story(树链剖分 点权 模版题)

#include <iostream> #include <algorithm> #include <cstring> #include <cmath> #include <queue> #include <map> #include <set> #include <vector> #include <cstdio> using namespace std; const int N=50010; s

HDU-3966 Aragorn&#39;s Story(树链剖分+线段树)

Aragorn's Story Time Limit: 10000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 12479    Accepted Submission(s): 3331 Problem Description Our protagonist is the handsome human prince Aragorn comes from The Lor