线段树合并总结
咕咕咕
原理
复杂度
两棵线段树合并复杂度即为两棵树公共节点数\(\times logn\),据说因为实际重合部分一般比较少,一次合并两棵线段树的复杂度近似为\(O(logn)\),所以合并\(n\)棵线段树复杂的为\(O(nlog_n)\)。
题
CF600E Lomsat gelral
一棵树有n个结点,每个结点都是一种颜色,每个颜色有一个编号,求树中每个子树的最多的颜色编号的和。
权值线段树,下标为颜色,维护区间答案和最多颜色编号和,然后自底向上合并即可。
#include <cstdio> #define MAXN 100010 #define sl tre[x].l #define sr tre[x].r #define ll long long using namespace std; int head[MAXN],vv[MAXN*2],nxt[MAXN*2],tot; inline void add_edge(int u, int v){ vv[++tot]=v; nxt[tot]=head[u]; head[u]=tot; } struct nod{ int l, r; ll ans, sum; }tre[MAXN*2*20]; int rt[MAXN],cnt,n; int col[MAXN]; ll res[MAXN]; void push_up(int x){ if(tre[sl].sum>tre[sr].sum){ tre[x].sum=tre[sl].sum; tre[x].ans=tre[sl].ans; }else if(tre[sl].sum<tre[sr].sum){ tre[x].sum=tre[sr].sum; tre[x].ans=tre[sr].ans; }else{ tre[x].sum=tre[sl].sum; tre[x].ans=tre[sl].ans+tre[sr].ans; } } int merge(int a, int b, int l, int r){ if(a==0) return b; if(b==0) return a; if(l==r){ tre[a].ans=l; tre[a].sum+=tre[b].sum; return a; } int mid=(l+r)>>1; tre[a].l=merge(tre[a].l, tre[b].l, l, mid); tre[a].r=merge(tre[a].r, tre[b].r, mid+1, r); push_up(a); return a; } void update(int &cur, int l, int r, int pos, int val){ if(cur==0) cur=++cnt; if(l==r){ tre[cur].sum+=(ll)val; tre[cur].ans=l; return; } int mid=(l+r)>>1; if(pos<=mid) update(tre[cur].l, l, mid, pos, val); else update(tre[cur].r, mid+1, r, pos, val); push_up(cur); } void dfs(int u, int f){ for(int i=head[u];i;i=nxt[i]){ int v=vv[i]; if(v==f) continue; dfs(v, u); merge(rt[u], rt[v], 1, 100000); } update(rt[u], 1, 100000, col[u], 1); res[u]=tre[rt[u]].ans; } int main(){ scanf("%d", &n); cnt=n; for(int i=1;i<=n;++i) scanf("%d", &col[i]),rt[i]=i; for(int i=1;i<=n-1;++i){ int u,v; scanf("%d %d", &u, &v); add_edge(u, v); add_edge(v, u); } dfs(1, 0); for(int i=1;i<=n;++i) printf("%lld ", res[i]); return 0; }