线段树合并:从入门到放弃

Posted kgxw0430

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树合并:从入门到放弃相关的知识,希望对你有一定的参考价值。


  • 感谢这篇博客(这里跳转)以及邱宇大神的讲解,我也算作入门(自闭)了。
  • 需要掌握的前置知识点:动态开点线段树、权值线段树。
一、合并思想

线段树合并,就是指建立一颗新的线段树,保存原有的两颗线段树的信息。

那么就是:

假设现在合并到了线段树的a、b某一个pos

如果a在这个区间有pos,b没有,那么新的线段树pos位置赋值为a

如果b在这个区间有pos,a没有,那么新的线段树pos位置赋值为b

如果a、b都有,那么继续

如果此时处理到了两颗线段树的叶子节点,就把b在pos的值加到a上,把新线段树上的pos位置赋值为a

递归处理左子树

递归处理右子树

用左右子树的值更新当前节点

将新线段树上的pos位置赋值为a,返回

二、复杂度证明
  • 这个我确实不太会,所以直接借用一下大佬博客的吧。(咕咕咕)
三、题目
  • 例题1:CF600E
  • 题目很简单,但应该是第一次尝试打合并,没有独立打完代码,而是看着题解代码打的。算作板子吧。
#include<bits/stdc++.h>
#define ll long long
#define lson tr[pos].l
#define rson tr[pos].r
using namespace std;
const int N=1e5+10;
const int inf=1e5;
int cnt,tot,n,a[N],rt[N],ver[N<<1],Next[N<<1],lin[N];ll anss[N];
struct tree{
    int sum,val,l,r;ll ans;
}tr[N*20];
void add(int x,int y){ver[++tot]=y;Next[tot]=lin[x];lin[x]=tot;}
void maintain(int pos){
    if(tr[lson].sum>tr[rson].sum){
        tr[pos].val=tr[lson].val;
        tr[pos].sum=tr[lson].sum;
        tr[pos].ans=tr[lson].ans;
    }else if(tr[lson].sum<tr[rson].sum){
        tr[pos].val=tr[rson].val;
        tr[pos].sum=tr[rson].sum;
        tr[pos].ans=tr[rson].ans;
    }else{
        tr[pos].val=tr[lson].val;
        tr[pos].sum=tr[lson].sum;
        tr[pos].ans=tr[lson].ans+tr[rson].ans;
    }
    return;
}
int merge(int a,int b,int l,int r){
    if(!b||!a) return a+b;
    if(l==r){
        tr[a].val=l;
        tr[a].sum+=tr[b].sum;
        tr[a].ans=l;
        return a;
    }
    int mid=l+r>>1;
    tr[a].l=merge(tr[a].l,tr[b].l,l,mid);
    tr[a].r=merge(tr[a].r,tr[b].r,mid+1,r);
    maintain(a);
    return a;
}
void update(int &pos,int l,int r,int now,int v){
    if(!pos){
        pos=++cnt;
    }
    if(l==r){
        tr[pos].sum++;
        tr[pos].val=l;
        tr[pos].ans=l;
        return ;
    }
    int mid=l+r>>1;
    if(now<=mid) update(tr[pos].l,l,mid,now,v);
    else update(tr[pos].r,mid+1,r,now,v);
    maintain(pos);
}
void dfs(int x,int f){
    for(int i=lin[x];i;i=Next[i]){
        int y=ver[i];
        if(y==f) continue;
        dfs(y,x);
        merge(rt[x],rt[y],1,inf);
    }
    update(rt[x],1,inf,a[x],1);
    anss[x]=tr[rt[x]].ans;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i) scanf("%d",&a[i]),rt[i]=++cnt;
    for(int i=1;i<n;++i){
        int x,y;scanf("%d%d",&x,&y);
        add(x,y);add(y,x);
    }
    dfs(1,0);
    for(int i=1;i<=n;++i) printf("%I64d ",anss[i]);
    cout<<endl;
    return 0;
}

题目先咕着,有空写。

以上

以上是关于线段树合并:从入门到放弃的主要内容,如果未能解决你的问题,请参考以下文章

线段树合并

POJ-3667 线段树区间合并入门题

超全面的线段树:从入门到入坟

线段树入门之惊鸿一瞥

线段树--从入门到精通

线段树合并