D12 轻重链剖分/树链剖分
重链剖分:按子树大小把树拆分成互不相交的若干条链。
每个节点只有 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"; } }
Luogu P5305 [GXOI/GZOI2019] 旧词
浙公网安备 33010602011771号