HDU 3966 Aragorn's Story(模板题)树链剖分+线段树

Posted 00isok

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDU 3966 Aragorn's Story(模板题)树链剖分+线段树相关的知识,希望对你有一定的参考价值。

<题目链接>

题目大意:

给定一颗带点权的树,进行两种操作,一是给定树上一段路径,对其上每个点的点权增加或者减少一个数,二是对某个编号点的点权进行查询。

解题分析:

树链剖分的模板题,还不会树链剖分可以看这里 >>>

 

#include <iostream>  
#include <cstring>  
#include <algorithm>  
#include <cstdio>  
#include <vector>  
#pragma comment(linker, "/STACK:1024000000,1024000000")    //手动扩栈
using namespace std;
typedef long long ll;
const int N=50005;
int siz[N],top[N],son[N],dep[N],tid[N],rk[N],fa[N];
//siz[]数组,用来保存以x为根的子树节点个数
//top[]数组,用来保存当前节点的所在链的顶端节点
//son[]数组,用来保存重儿子
//dep[]数组,用来保存当前节点的深度
//fa[]数组,用来保存当前节点的父亲
//tid[]数组,用来保存树中每个节点剖分后的新编号
//rk[]数组,线段树中编号对应的原节点编号 
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
int sum[4*N],lazy[4*N];
int cnt,tim,n,m,p,a[N];
struct EDGE
{
    int next,to;
}edge[N*2];
int head[N];
void init()
{
    cnt=tim=0;
    memset(son,-1,sizeof(son));
    memset(head,-1,sizeof(head));
}
void add(int u,int v)
{
    edge[++cnt].to=v;
    edge[cnt].next=head[u];
    head[u]=cnt;
}

void dfs1(int u,int pre/*fa*/,int de)  /*求出每个点的深度和父节点,和重儿子编号*/
{
    dep[u]=de;fa[u]=pre;siz[u]=1;
    for(int i=head[u]; ~i; i=edge[i].next)
    {
        int v=edge[i].to;
        if(v!=pre)
        {
            dfs1(v,u,de+1);
            siz[u]+=siz[v];
            if(son[u]==-1||siz[v]>siz[son[u]])
                son[u]=v;     //得到u的重儿子
        }
    }
}

void dfs2(int u,int tp)  /*给链编号,形成一一对应的关系*/
{
    top[u]=tp;          //将该节点所在链的顶端置为tp
    tid[u]=++tim;       //用来保存树中每个节点剖分后的新编号
    rk[tid[u]]=u;       //线段树中编号对应的原节点编号 
    if(son[u]==-1) return;
    dfs2(son[u],tp);  //这里用来连接重链
    for(int i=head[u]; ~i; i=edge[i].next)
    {
        int v=edge[i].to;
        if(v!=son[u]/*不是u的重儿子*/&&v!=fa[u]/*不是u的父节点*/)
            dfs2(v,v);  //不能加入当前重链上的节点,以该节点为链首,向下拉一条新的重链
    }
}

void Pushup(int rt)
{
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}

void Pushdown(int rt,int len)    //将懒惰标记下放
{
    if(lazy[rt])
    {
        lazy[rt<<1]+=lazy[rt];  
        lazy[rt<<1|1]+=lazy[rt];  
        sum[rt<<1]+=lazy[rt]*(len-(len>>1));  
        sum[rt<<1|1]+=lazy[rt]*(len>>1);  
        lazy[rt]=0;  
    }
}
void build(int l,int r,int rt)
{
    lazy[rt]=0;
    if(l==r)
    {
        sum[rt]=a[rk[l]];//初始值为原编号的数量
        return;
    }
    int mid=(l+r)>>1;
    build(lson);
    build(rson);
    Pushup(rt);
}
void update(int L,int R,int val,int l,int r,int rt)    //线段树区间修改
{
    if(L<=l&&r<=R)
    {
        lazy[rt]+=val;
        sum[rt]+=val*(r-l+1);
        return;
    }
    Pushdown(rt,r-l+1);
    int mid=(l+r)>>1;
    if(mid>=L) update(L,R,val,lson);
    if(mid<R) update(L,R,val,rson);
    Pushup(rt);
}

int query(int pos,int l,int r,int rt)    //单点查询
{
    if(l==r){
        return sum[rt];
    }  
    Pushdown(rt,r-l+1);  
    int mid=(l+r)>>1; 
    int ans=0; 
    if(pos<=mid)  ans+=query(pos,lson);  
    else  ans+=query(pos,rson);
    return ans;  
}

/*
如果在同一条链上,那么他们在线段树中的坐标一定是连续的
如果不在一条链上,那么比较x,y点链顶的父节点深度,更新深度深的链(进行交换,把深度深的赋予为x),更新完后,将范围调整到y--fa[top[x]],然后一直迭代,直到在同一个链上。
*/
void Change(int x,int y,int val)  
{  
    while(top[x]!=top[y])   //如果x和y不在同一条链上
    {  
        if(dep[top[x]]<dep[top[y]]) swap(x,y);  //把深度更深的赋给x
        update(tid[top[x]],tid[x],val,1,n,1);  
        x=fa[top[x]];  
    }  
    if(dep[x]>dep[y])swap(x,y);    //改成  if(tid[x]>tid[y])swap(x,y);  也可以
    update(tid[x],tid[y],val,1,n,1);  
}  

int main()
{
    while(~scanf("%d%d%d",&n,&m,&p))
    {
        init();
        for(int i=1; i<=n; i++)
            scanf("%d",&a[i]);
        while(m--)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            add(u,v); add(v,u);
        }
        dfs1(1,0,0);    /*求出每个点的深度和父节点,和重儿子编号*/
        dfs2(1,1);      /*给链编号,形成一一对应的关系*/
        build(1,n,1);   //用线段树来维护剖分出的树链  
        while(p--)
        {
            char oper[2];
            int aa,b,c;
            scanf("%s",oper);
            if(oper[0]==‘Q‘)
            {
                scanf("%d",&aa);
                printf("%d
",query(tid[aa],1,n,1));
            }
            else
            {
                scanf("%d%d%d",&aa,&b,&c);
                if(oper[0]==‘D‘) c=-c;
                Change(aa,b,c);
            }
        }
    }
}

  

2018-09-09

以上是关于HDU 3966 Aragorn's Story(模板题)树链剖分+线段树的主要内容,如果未能解决你的问题,请参考以下文章

hdu 3966 Aragorn's Story

AC日记——Aragorn's Story HDU 3966

HDU 3966 Aragorn's Story

HDU - 3966 Aragorn's Story(树链剖分入门+线段树)

HDU-3966 Aragorn's Story(树链剖分+线段树)

HDU 3966 Aragorn's Story 树链剖分+BIT区间修改/单点询问