線段樹合併(雨天的尾巴 題解)
阿新 • • 發佈:2020-10-22
線段樹合併
前置知識:權值線段樹,動態開點;
其實動態開點就是當前節點的左右子區間的節點編號不是單純的2倍和2倍加1的關係;
我們需要儲存一下左右子區間的節點編號,就可以了;
線段樹合併,顧名思義,就是,將兩顆值域相同權值線段樹合併為一棵線段樹,並且對於相同節點
相加的一些操作,具體在程式碼中講解;
模板題
雨天的尾巴
#include<iostream> #include<cstdio> #define l(o) (t[o].ls) #define r(o) (t[o].rs) #define mid ((l+r)>>1) using namespace std; const int N=1e5+7; const int M=1e5; struct edge{ int v,nxt; }e[N<<1]; struct Setment{ int ls,rs,dat,id; }t[N*20*4];//注意線段樹大小是不確定的,一般開這麼多 int n,m,cnt,tot; int head[N],dep[N],top[N],siz[N],hs[N],fa[N],ans[N],rt[N]; void add_edge(int u,int v){ e[++cnt]=(edge){v,head[u]}; head[u]=cnt; } void get_tree(int u){ dep[u]=dep[fa[u]]+1; siz[u]=1; for(int i=head[u];i;i=e[i].nxt){ int to=e[i].v; if(to!=fa[u]){ fa[to]=u; get_tree(to); siz[u]+=siz[to]; if(siz[hs[u]]<siz[to]) hs[u]=to; } } } void dfs(int u,int fat){ top[u]=fat; if(hs[u]) dfs(hs[u],fat); for(int i=head[u];i;i=e[i].nxt){ int to=e[i].v; if(to!=fa[u]&&to!=hs[u]){ dfs(to,to); } } } int lca(int x,int y){ while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]]) swap(x,y); x=fa[top[x]]; } return dep[x]<dep[y]?x:y; } //以上為求lca操作 void up(int o){ t[o].dat=max(t[l(o)].dat,t[r(o)].dat); t[o].id=t[o].dat==t[l(o)].dat?t[l(o)].id:t[r(o)].id;//儘量選小的 } void insert(int &o,int l,int r,int k,int val){ if(!o) o=++tot;//動態開點,注意這裡的取地址符號,是為了把編號存下來 if(l==r){ t[o].dat+=val; t[o].id=k; return; } if(k<=mid) insert(l(o),l,mid,k,val); if(k>mid) insert(r(o),mid+1,r,k,val); up(o); } void merge(int &x,int y,int l,int r){ if(!x) { x=y; return; } if(!y) return; if(l==r){ t[x].dat+=t[y].dat; return; } merge(t[x].ls,t[y].ls,l,mid); merge(t[x].rs,t[y].rs,mid+1,r); up(x); } void get_ans(int u){ for(int i=head[u];i;i=e[i].nxt){ int to=e[i].v; if(to!=fa[u]){ get_ans(to); merge(rt[u],rt[to],1,M); } } ans[u]=t[rt[u]].id; } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<n;i++){ int x,y; scanf("%d%d",&x,&y); add_edge(x,y); add_edge(y,x); } get_tree(1); dfs(1,1); for(int i=1;i<=m;i++){ int x,y,z; scanf("%d%d%d",&x,&y,&z); // cout<<"ffff"<<"\n"; int ff=lca(x,y); // cout<<"kkkkk"<<"\n"; insert(rt[x],0,M,z,1);//rt[]陣列表示為以x為根節點的線段樹的最大區間; insert(rt[y],0,M,z,1); insert(rt[ff],0,M,z,-1); insert(rt[fa[ff]],0,M,z,-1);//點差分 } get_ans(1); for(int i=1;i<=n;i++) cout<<ans[i]<<"\n"; } /* 10 10 2 1 3 2 4 3 5 3 6 3 7 4 8 5 9 8 10 3 6 2 6 3 2 6 3 3 2 6 6 6 10 3 3 10 7 1 3 7 4 7 9 5 4 2 4 3 3 6 0 6 6 4 5 6 1 5 5 1 */