【學習筆記】線段樹合併
阿新 • • 發佈:2020-11-02
具體地,線段樹合併是指合併兩棵動態開點權值線段樹。
考慮合併過程,將樹\(x\)合併到\(y.\)
噹噹前位置兩棵樹中有一者為空的時候,可以直接繼承當前節點返回。
若到葉子的時候,直接合並資訊。
然後對當前樹\(x\)的左右葉子合併即可。
int merge(int x,int y,int l,int r){ if(!x||!y)return x|y; if(l==r){sum[x]+=sum[y];tag[x]=l;return x;} int mid=(l+r)>>1; ls[x]=merge(ls[x],ls[y],l,mid); rs[x]=merge(rs[x],rs[y],mid+1,r); pushup(x);return x; }
對於程式碼中變數的解釋:
\(x\)是樹中節點的編號。\(sum[x]\)表示\(x\)點處答案的出現次數,\(tag[x]\)表示\(x\)處的答案。
答案記錄為出現次數最多的物品編號。
樹上更新資訊時,考慮左子樹優先(即左子樹大於等於)來保證編號儘量小。繼承的時候\(sum,tag\)要一起更新。
樹上修改的時候,由於是單點修改,所以到葉子的時候更新資訊,回來的時候\(pushup\)即可。
樹上差分:對於路徑\(<x,y>,\)我們考慮進行\(cnt[x]+1,cnt[y]+1,cnt[LCA]-1,cnt[fa[LCA]]-1.\)最後進行一次\(dfs\)合併答案。
求\(LCA\)
注意理解程式碼中的\(x,\)裡面是陣列版本的線段樹,所以函式中需要傳區間引數\([l,r].\)
注意理解程式碼中\(x,sum,tag\)的含義,是理解重點。
#include<bits/stdc++.h> using namespace std; const int MAXN=6e6+10; const int N=1e5+10; int head[N],tot,cnt,n,m; int ls[MAXN],rs[MAXN],f[N][21]; int dep[N],pa[N],ans[N]; struct E{int nxt,to;}e[N<<1]; inline void add(int x,int y){e[++tot]=(E){head[x],y};head[x]=tot;} int sum[MAXN],tag[MAXN],rt[MAXN],R=100000; void dfs(int x,int fa){ pa[x]=f[x][0]=fa; dep[x]=dep[fa]+1; for(int i=1;i<=20;++i)f[x][i]=f[f[x][i-1]][i-1]; for(int i=head[x];i;i=e[i].nxt){ int j=e[i].to; if(j==fa)continue; dfs(j,x); } } inline void pushup(int x){ if(sum[ls[x]]>=sum[rs[x]])sum[x]=sum[ls[x]],tag[x]=tag[ls[x]]; else sum[x]=sum[rs[x]],tag[x]=tag[rs[x]]; } int LCA(int x,int y){ if(dep[x]<dep[y])swap(x,y); for(int i=20;i>=0;--i) if(dep[f[x][i]]>=dep[y])x=f[x][i]; if(x==y)return x; for(int i=20;i>=0;--i) if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i]; return f[x][0]; } int change(int x,int l,int r,int pos,int v){ if(!x)x=++cnt; if(l==r){ sum[x]+=v; tag[x]=l; return x; } int mid=(l+r)>>1; if(pos<=mid)ls[x]=change(ls[x],l,mid,pos,v); else rs[x]=change(rs[x],mid+1,r,pos,v); pushup(x);return x; } int merge(int x,int y,int l,int r){ if(!x||!y)return x|y; if(l==r){sum[x]+=sum[y];tag[x]=l;return x;} int mid=(l+r)>>1; ls[x]=merge(ls[x],ls[y],l,mid); rs[x]=merge(rs[x],rs[y],mid+1,r); pushup(x);return x; } void redfs(int x){ for(int i=head[x];i;i=e[i].nxt){ int j=e[i].to; if(j==pa[x])continue; redfs(j); rt[x]=merge(rt[x],rt[j],1,R); } if(sum[rt[x]])ans[x]=tag[rt[x]]; } int main(){ scanf("%d%d",&n,&m); for(int i=1,x,y;i<n;++i){ scanf("%d%d",&x,&y); add(x,y);add(y,x); } dfs(1,0); for(int i=1,x,y,z;i<=m;++i){ scanf("%d%d%d",&x,&y,&z); int L=LCA(x,y); rt[x]=change(rt[x],1,R,z,1); rt[y]=change(rt[y],1,R,z,1); rt[L]=change(rt[L],1,R,z,-1); if(pa[L])rt[pa[L]]=change(rt[pa[L]],1,R,z,-1); } redfs(1); for(int i=1;i<=n;++i)printf("%d\n",ans[i]); return 0; }