【bzoj4372】 烁烁的游戏【动态树分治】

烁烁的游戏

Description

背景:烁烁很喜欢爬树,这吓坏了树上的皮皮鼠。
题意:
给定一颗n个节点的树,边权均为1,初始树上没有皮皮鼠。
烁烁他每次会跳到一个节点u,把周围与他距离不超过d的节点各吸引出w只皮皮鼠。皮皮鼠会被烁烁吸引,所以会一直待在节点上不动。
烁烁很好奇,在当前时刻,节点u有多少个他的好朋友—皮皮鼠。
大意:
给一颗n个节点的树,边权均为1,初始点权均为0,m次操作:
Q x:询问x的点权。
M x d w:将树上与节点x距离不超过d的节点的点权均加上w。

Input

第一行两个正整数:n,m
接下来的n-1行,每行三个正整数u,v,代表u,v之间有一条边。
接下来的m行,每行给出上述两种操作中的一种。

Output

对于每个Q操作,输出当前x节点的皮皮鼠数量。

解法:

先考虑暴力。对于每一次查询,把整棵树遍历一遍,对于每个遍历到的节点,加上当前节点到查询点的距离的所有的修改值。最后得到的就是这个点的点权。
有了思路,就上树分治啦!每个点开2棵可区间修改的李超线段树。
第一棵线段树:以子树中的每一个点到自己的距离为下标,维护每一种距离的修改值。
第二棵线段树:以子树中的每一个点到自己的父亲的距离为下标,维护每一种距离的修改值。
修改:设要修改的点为u,要与u距离不超过d1的所有点加上w。在向上爬的过程中,设当前爬到节点为i,u到fa[i]的距离为d2。则将fa[i]的第一棵线段树下标为0~d1-d2都加上w,再将用于去掉重复的i的第二棵线段树下标为0~d1-d2都加上w。
查询:其实统计答案的原理和暴力是一样的。在向上爬的过程中,设当前点的父亲到查询点的距离为d。对于每个祖先节点,答案加上这个点的父亲的第一棵线段树下标为d的修改值,再减去这个点的第二棵线段树下标为d的修改值,目的仍然是减去重复的。这大概就是动态树分治的一种套路吧。
于是搞定了。
代码:

#include<cstdio>
#include<algorithm>
using namespace std;
const int N=100005;
int n,m,u,v,d,cnt,head[N],to[N*2],nxt[N*2];
int idx,lg2[N*2],dfn[N],dep[N],siz[N],pos[N*2],f[N*2][20];
int mi,size,rt,fa[N];
int tot,root[N][2],tag[20000005],ch[20000005][2];
bool vis[N];
char s[5];
void adde(int u,int v){
    to[++cnt]=v;
    nxt[cnt]=head[u];
    head[u]=cnt;
}
void dfs(int pre,int u){
    dfn[u]=++idx;
    pos[idx]=u;
    int v;
    for(int i=head[u];i;i=nxt[i]){
        v=to[i];
        if(v!=pre){
            dep[v]=dep[u]+1;
            dfs(u,v);
            pos[++idx]=u;
        }
    }
}
void st(){
    for(int i=1;i<=idx;i++){
        f[i][0]=pos[i];
    }
    for(int j=1;j<=20;j++){
        for(int i=1;i+(1<<j)-1<=idx;i++){
            if(dep[f[i][j-1]]<dep[f[i+(1<<(j-1))][j-1]]){
                f[i][j]=f[i][j-1];
            }else{
                f[i][j]=f[i+(1<<(j-1))][j-1];
            }
        }
    }
}
int lca(int u,int v){
    if(dfn[u]>dfn[v]){
        swap(u,v);
    }
    int k=lg2[dfn[v]-dfn[u]];
    if(dep[f[dfn[u]][k]]<dep[f[dfn[v]-(1<<k)+1][k]]){
        return f[dfn[u]][k];
    }else{
        return f[dfn[v]-(1<<k)+1][k];
    }
}
void dfsroot(int pre,int u){
    siz[u]=1;
    int v,mx=0;
    for(int i=head[u];i;i=nxt[i]){
        v=to[i];
        if(v!=pre&&!vis[v]){
            dfsroot(u,v);
            siz[u]+=siz[v];
            mx=max(mx,siz[v]);
        }
    }
    mx=max(mx,size-siz[u]);
    if(mx<mi){
        mi=mx;
        rt=u;
    }
}
int dis(int u,int v){
    return dep[u]+dep[v]-2*dep[lca(u,v)];
}
void build(int pre,int u){
    vis[u]=true;
    fa[u]=pre;
    int v;
    for(int i=head[u];i;i=nxt[i]){
        v=to[i];
        if(!vis[v]){
            mi=size=siz[v];
            dfsroot(u,v);
            build(u,rt);
        }
    }
}
void upd(int &o,int l,int r,int L,int R,int v){
    if(!o){
        o=++tot;
    }
    if(L==l&&R==r){
        tag[o]+=v;
        return;
    }
    int mid=(l+r)/2;
    if(R<=mid){
        upd(ch[o][0],l,mid,L,R,v);
    }else if(L>mid){
        upd(ch[o][1],mid+1,r,L,R,v);
    }else{
        upd(ch[o][0],l,mid,L,mid,v);
        upd(ch[o][1],mid+1,r,mid+1,R,v);
    }
}
int qry(int o,int l,int r,int k){
    if(!o){
        return 0;
    }
    if(l==r){
        return tag[o];
    }
    int mid=(l+r)/2;
    if(k<=mid){
        return tag[o]+qry(ch[o][0],l,mid,k);
    }{
        return tag[o]+qry(ch[o][1],mid+1,r,k);
    }
}
int query(int u){
    int ret=qry(root[u][0],0,n,0),tmp;
    for(int i=u;fa[i];i=fa[i]){
        tmp=dis(fa[i],u);
        ret+=qry(root[fa[i]][0],0,n,tmp)-qry(root[i][1],0,n,tmp);
    }
    return ret;
}
void update(int u,int d,int w){
    upd(root[u][0],0,n,0,d,w);
    int tmp;
    for(int i=u;fa[i];i=fa[i]){
        tmp=dis(fa[i],u);
        if(tmp>d){
            continue;
        }
        upd(root[fa[i]][0],0,n,0,d-tmp,w);
        upd(root[i][1],0,n,0,d-tmp,w);
    }
}
int main(){
    for(int i=2;i<=200000;i++){
        lg2[i]=lg2[i/2]+1;
    }
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++){
        scanf("%d%d",&u,&v);
        adde(u,v);
        adde(v,u);
    }
    dfs(0,1);
    st();
    mi=size=n;
    dfsroot(0,1);
    build(0,rt);
    for(int i=1;i<=m;i++){
        scanf("%s%d",s,&u);
        if(s[0]=='Q'){
            printf("%d\n",query(u));              
        }else{
            scanf("%d%d",&d,&v);
            update(u,d,v);                                             
        }
    }
    return 0;
}
posted @ 2017-12-20 13:57  一剑霜寒十四洲  阅读(127)  评论(0编辑  收藏  举报