D12 轻重链剖分/树链剖分

D12 轻重链剖分/树链剖分_哔哩哔哩_bilibili

P3384 【模板】重链剖分/树链剖分 - 洛谷

重链剖分:按子树大小把树拆分成互不相交的若干条链。

每个节点只有 1 个(或 0 个)重儿子,其余都是轻儿子。

每条链的链头是轻儿子,其余为重儿子。

1. dfs1(u,f) 预处理 fa[],dep[],siz[],son[]

2. dfs2(u,t) 预处理 top[],id[],nw[]

3. 把树拆成链,用链建线段树

4. 路径拆成链,线段树上做区修、区查

// 树链剖分 O(mlognlogn)
#include<bits/stdc++.h>
using namespace std;

#define int long long
const int N=100010;
int n,m,root,P,w[N],a,b,c,t;
vector<int> e[N];
int fa[N],dep[N],siz[N],son[N];
int top[N],id[N],nw[N],cnt; //树链

#define lc u<<1
#define rc u<<1|1
struct tree{
  int l,r; 
  int sum,add;
}tr[N*4]; //线段树

void dfs1(int u,int f){ //搞fa,dep,siz,son
  fa[u]=f,dep[u]=dep[f]+1,siz[u]=1;
  for(int v:e[u]){
    if(v==f) continue;
    dfs1(v,u);
    siz[u]+=siz[v];
    if(siz[son[u]]<siz[v]) son[u]=v; 
  }
}
void dfs2(int u,int tp){ //搞top,id,nw
  top[u]=tp,id[u]=++cnt,nw[cnt]=w[u];
  if(son[u]) dfs2(son[u],tp);
  for(int v:e[u]){
    if(v==fa[u]||v==son[u])continue;
    dfs2(v,v);
  }
}

void pushup(int u){
  tr[u].sum=tr[lc].sum+tr[rc].sum;
}
void pushdown(int u){
  if(tr[u].add){
    tr[lc].sum+=tr[u].add*(tr[lc].r-tr[lc].l+1);
    tr[rc].sum+=tr[u].add*(tr[rc].r-tr[rc].l+1);
    tr[lc].add+=tr[u].add;
    tr[rc].add+=tr[u].add;
    tr[u].add=0;
  }
}
void build(int u,int l,int r){ //构建线段树
  tr[u]={l,r,nw[l],0};
  if(l==r) return;
  int mid=l+r>>1;
  build(lc,l,mid);
  build(rc,mid+1,r);
  pushup(u);
}
void change(int u,int x,int y,int k){ //线段树修改
  if(x>tr[u].r||y<tr[u].l) return;
  if(x<=tr[u].l&&tr[u].r<=y){
    tr[u].sum+=k*(tr[u].r-tr[u].l+1);
    tr[u].add+=k;
    return;
  }
  pushdown(u);
  change(lc,x,y,k);
  change(rc,x,y,k);
  pushup(u);
}
void change_path(int u,int v,int k){ //修改路径
  while(top[u]!=top[v]){
    if(dep[top[u]]<dep[top[v]]) swap(u,v);
    change(1,id[top[u]],id[u],k);
    u=fa[top[u]];
  }
  if(dep[u]<dep[v]) swap(u,v);
  change(1,id[v],id[u],k); //最后一段
}
void change_tree(int u,int k){ //修改子树
  change(1,id[u],id[u]+siz[u]-1,k);
}
int query(int u,int x,int y){ //线段树查询
  if(x>tr[u].r||y<tr[u].l) return 0;
  if(x<=tr[u].l&&tr[u].r<=y) return tr[u].sum;
  pushdown(u);
  return query(lc,x,y)+query(rc,x,y);
}
int query_path(int u,int v){ //查询路径
  int res=0;
  while(top[u]!=top[v]){
    if(dep[top[u]]<dep[top[v]]) swap(u,v);
    res+=query(1,id[top[u]],id[u]);
    u=fa[top[u]];
  }
  if(dep[u]<dep[v]) swap(u,v);
  res+=query(1,id[v],id[u]); //最后一段
  return res;
}
int query_tree(int u){ //查询子树
  return query(1,id[u],id[u]+siz[u]-1);
}
signed main(){
  ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
  cin>>n>>m>>root>>P;
  for(int i=1; i<=n; i++) cin>>w[i];
  for(int i=0; i<n-1; i++){
    cin>>a>>b;
    e[a].push_back(b); e[b].push_back(a);
  }
  dfs1(root,0); dfs2(root,root); //把树拆成链
  build(1,1,n);  //用链建线段树
  while(m--){
    cin>>t>>a;
    if(t==1) cin>>b>>c,change_path(a,b,c);
    else if(t==3) cin>>c,change_tree(a,c);
    else if(t==2) cin>>b,cout<<query_path(a,b)%P<<"\n";
    else cout<<query_tree(a)%P<<"\n";
  }
}

 

// 树链剖分 O(mlognlogn)
#include<bits/stdc++.h>
using namespace std;

#define int long long
const int N=100010;
int n,m,root,P,w[N],a,b,c,t;
vector<int> e[N];
int fa[N],dep[N],siz[N],son[N];
int top[N],id[N],nw[N],cnt; //树链

#define lc u<<1
#define rc u<<1|1
int sum[N<<2],add[N<<2]; //线段树

void dfs1(int u,int f){ //搞fa,dep,siz,son
  fa[u]=f,dep[u]=dep[f]+1,siz[u]=1;
  for(int v:e[u]){
    if(v==f) continue;
    dfs1(v,u);
    siz[u]+=siz[v];
    if(siz[son[u]]<siz[v]) son[u]=v; 
  }
}
void dfs2(int u,int tp){ //搞top,id,nw
  top[u]=tp,id[u]=++cnt,nw[cnt]=w[u];
  if(!son[u]) return;
  dfs2(son[u],tp);
  for(int v:e[u]){
    if(v==fa[u]||v==son[u])continue;
    dfs2(v,v);
  }
}

void pushup(int u){
  sum[u]=sum[lc]+sum[rc];
}
void pushdown(int u,int l,int r,int mid){
  if(add[u]){
    sum[lc]+=add[u]*(mid-l+1);
    sum[rc]+=add[u]*(r-mid);
    add[lc]+=add[u];
    add[rc]+=add[u];
    add[u]=0;
  }
}
void build(int u,int l,int r){ //构建线段树
  sum[u]=nw[l]; 
  if(l==r) return;
  int mid=l+r>>1;
  build(lc,l,mid);
  build(rc,mid+1,r);
  pushup(u);
}
void change(int u,int l,int r,int x,int y,int k){ //线段树修改
  if(x>r||y<l) return;
  if(x<=l&&r<=y){
    sum[u]+=k*(r-l+1);
    add[u]+=k;
    return;
  }
  int mid=l+r>>1;
  pushdown(u,l,r,mid);
  change(lc,l,mid,x,y,k);
  change(rc,mid+1,r,x,y,k);
  pushup(u);
}
void change_path(int u,int v,int k){ //修改路径
  while(top[u]!=top[v]){
    if(dep[top[u]]<dep[top[v]]) swap(u,v);
    change(1,1,n,id[top[u]],id[u],k);
    u=fa[top[u]];
  }
  if(dep[u]<dep[v]) swap(u,v);
  change(1,1,n,id[v],id[u],k); //最后一段
}
void change_tree(int u,int k){ //修改子树
  change(1,1,n,id[u],id[u]+siz[u]-1,k);
}
int query(int u,int l,int r,int x,int y){ //线段树查询
  if(x>r||y<l) return 0;
  if(x<=l&&r<=y) return sum[u];
  int mid=l+r>>1;
  pushdown(u,l,r,mid);
  return query(lc,l,mid,x,y)+query(rc,mid+1,r,x,y);
}
int query_path(int u,int v){ //查询路径
  int res=0;
  while(top[u]!=top[v]){
    if(dep[top[u]]<dep[top[v]]) swap(u,v);
    res+=query(1,1,n,id[top[u]],id[u]);
    u=fa[top[u]];
  }
  if(dep[u]<dep[v]) swap(u,v);
  res+=query(1,1,n,id[v],id[u]); //最后一段
  return res;
}
int query_tree(int u){ //查询子树
  return query(1,1,n,id[u],id[u]+siz[u]-1);
}
signed main(){
  ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
  cin>>n>>m>>root>>P;
  for(int i=1; i<=n; i++) cin>>w[i];
  for(int i=0; i<n-1; i++){
    cin>>a>>b;
    e[a].push_back(b); e[b].push_back(a);
  }
  dfs1(root,0); dfs2(root,root); //把树拆成链
  build(1,1,n);  //用链建线段树
  while(m--){
    cin>>t>>a;
    if(t==1) cin>>b>>c,change_path(a,b,c);
    else if(t==3) cin>>c,change_tree(a,c);
    else if(t==2) cin>>b,cout<<query_path(a,b)%P<<"\n";
    else cout<<query_tree(a)%P<<"\n";
  }
}

 

树链剖分 - OI Wiki

Luogu P2486 [SDOI2011] 染色

Luogu P4211 [LNOI2014] LCA

Luogu P3313 [SDOI2014] 旅行

Luogu P4219 [BJOI2014] 大融合

Luogu P4216 [SCOI2015] 情报传递

Luogu P2146 [NOI2015] 软件包管理器

Luogu P5305 [GXOI/GZOI2019] 旧词

Luogu P1505 [国家集训队] 旅游

 

posted @ 2022-05-28 13:26  董晓  阅读(2187)  评论(2)    收藏  举报