线段树合并总结

孤街浪徒 提交于 2019-11-30 16:10:23

线段树合并总结

咕咕咕

原理

复杂度

两棵线段树合并复杂度即为两棵树公共节点数\(\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;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!