线段树合并学习笔记

线段树合并用于解决一些需要将两颗线段树合并的题目

常见于一些子树处理的题目。

动态开点,记录左右子树。

不得不说,merge的代码像极了FHQtreap

inline int merge(int a,int b,int x,int y){
      if(!a)return b;
      if(!b)return a;
      if(x==y)/*do whatever you need and return a*/
      int mid=(x+y)>>1;
      tr[a].l=merge(tr[a].l,tr[b].l,x,mid);
      tr[a].r=merge(tr[a].r,tr[b].r,mid+1,y);
      pushup(a);
      return a;  
}

然后是更新,其实代码很好写……

inline void update(int &now,int l,int r,int x){
        if(!now)now=++cnt;
        if(l==r)/*do whatever you need*/
        int mid=(l+r)>>1;
        if(mid>=x)update(tr[now].l,l,mid,x);
        else update(tr[now].r,mid+1,r,x);
        pushup(now);
}

然后推荐几道例题:

1.CF600E

思路:对于每一个节点,它的线段树等于所有子节点线段树的合并在加上它自己。

代码如下:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=2*1e5+10;
inline int read(){
    int x=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;char c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
    return x*f;
}
struct node{
    int l,r,val,sum,ans;
}tr[maxn*25];
int n,c[maxn],root[maxn],cnt,ans[maxn];
int beg[maxn],nex[maxn<<1],to[maxn<<1],e;
inline void add(int x,int y){
    e++;nex[e]=beg[x];
    beg[x]=e;to[e]=y;
}
inline void pushup(int now){
    int l=tr[now].l,r=tr[now].r;
    if(tr[l].sum>tr[r].sum){
        tr[now].ans=tr[l].ans;
        tr[now].sum=tr[l].sum;
        tr[now].val=tr[l].val;
    }
    if(tr[l].sum<tr[r].sum){
        tr[now].ans=tr[r].ans;
        tr[now].sum=tr[r].sum;
        tr[now].val=tr[r].val;
    }
    if(tr[l].sum==tr[r].sum){
        tr[now].ans=tr[l].ans+tr[r].ans;
        tr[now].sum=tr[l].sum;
        tr[now].val=tr[l].val;
    }
}
inline void update(int &now,int l,int r,int x){
    if(!now)now=++cnt;
    if(l==r){
        tr[now].val=l;
        tr[now].sum++;
        tr[now].ans=l;
        return;
    }
    int mid=(l+r)>>1;
    if(mid>=x)update(tr[now].l,l,mid,x);
    else update(tr[now].r,mid+1,r,x);
    pushup(now);
}
inline int merge(int a,int b,int l,int r){
    if(!a)return b;
    if(!b)return a;
    if(l==r){
        tr[a].ans=tr[a].val=l;
        tr[a].sum+=tr[b].sum;
        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);
    pushup(a);
    return a;
}
inline void dfs(int fa,int x){
    for(int i=beg[x];i;i=nex[i]){
        int t=to[i];
        if(t==fa)continue;
        dfs(x,t);
        merge(root[x],root[t],1,n);
    }
    update(root[x],1,n,c[x]);
    ans[x]=tr[root[x]].ans;
}
signed main(){
    n=read();
    for(int i=1;i<=n;i++){
        c[i]=read();
        root[i]=i;
    }
    int x,y;
    for(int i=1;i<n;i++){
        x=read(),y=read();
        add(x,y),add(y,x);
    }
    cnt=n;
    dfs(0,1);
    for(int i=1;i<=n;i++)
        printf("%lld ",ans[i]);
    puts("");
    return 0;
} 

2.P3605

思路:1.首先离散化(常规套路);

      2.建权值线段树;

      3.合并子节点(也是常规套路);

代码如下:

//p3605,线段树合并,score=100
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int n,p[maxn],tmp[maxn],ans[maxn],rt[maxn],cnt;
int beg[maxn],nex[maxn<<1],to[maxn<<1],e;
inline int read(){
    int x=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
    return x*f;
}
inline void add(int x,int y){
    e++;nex[e]=beg[x];
    beg[x]=e;to[e]=y;
}
struct node{
    int l,r,val;
}tr[maxn*50];
inline int query(int h,int l,int r,int x,int y){
    if(l>y||r<x)return 0;
    if(l>=x&&r<=y)return tr[h].val;
    int mid=(l+r)>>1;
    return query(tr[h].l,l,mid,x,y)+query(tr[h].r,mid+1,r,x,y);
}
inline void update(int &now,int l,int r,int x){
    if(!now)now=++cnt;
    if(l==r){
        tr[now].val++;
        return;
    }
    int mid=(l+r)>>1;
    if(mid>=x)update(tr[now].l,l,mid,x);
    else update(tr[now].r,mid+1,r,x);
    tr[now].val=tr[tr[now].l].val+tr[tr[now].r].val;
} 
inline int merge(int a,int b,int l,int r){
    if(!a)return b;
    if(!b)return a;
    if(l==r){
        tr[a].val+=tr[b].val;
        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);
    tr[a].val=tr[tr[a].l].val+tr[tr[a].r].val;
    return a;
}
inline void dfs(int x,int fa){
    for(int i=beg[x];i;i=nex[i]){
        int t=to[i];
        if(t==fa)continue;
        dfs(t,x);
        merge(rt[x],rt[t],1,n);
    }
    ans[x]=query(rt[x],1,n,p[x],n);
    update(rt[x],1,n,p[x]);
}
int main(){
    n=read();
    for(int i=1;i<=n;i++){
        tmp[i]=p[i]=read();
        rt[i]=i;
    }
    sort(tmp+1,tmp+1+n);
    for(int i=1;i<=n;i++)
        p[i]=lower_bound(tmp+1,tmp+1+n,p[i])-tmp;
    int x;
    for(int i=2;i<=n;i++){
        x=read();
        add(i,x),add(x,i);
    }
    cnt=n;
    dfs(1,0);
    for(int i=1;i<=n;i++)
        printf("%d\n",ans[i]);
    return 0;
}

3.雨天的尾巴

有了线段树合并的基础后,很容易想到差分最后线段树合并。

这道题跟hdu上的relief grain是重题,然而我们老师却把那道题当做树剖的练手题给我们。

害得我想了好久……

代码如下:

//p3605,线段树合并,score=100
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int n,p[maxn],tmp[maxn],ans[maxn],rt[maxn],cnt;
int beg[maxn],nex[maxn<<1],to[maxn<<1],e;
inline int read(){
    int x=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
    return x*f;
}
inline void add(int x,int y){
    e++;nex[e]=beg[x];
    beg[x]=e;to[e]=y;
}
struct node{
    int l,r,val;
}tr[maxn*50];
inline int query(int h,int l,int r,int x,int y){
    if(l>y||r<x)return 0;
    if(l>=x&&r<=y)return tr[h].val;
    int mid=(l+r)>>1;
    return query(tr[h].l,l,mid,x,y)+query(tr[h].r,mid+1,r,x,y);
}
inline void update(int &now,int l,int r,int x){
    if(!now)now=++cnt;
    if(l==r){
        tr[now].val++;
        return;
    }
    int mid=(l+r)>>1;
    if(mid>=x)update(tr[now].l,l,mid,x);
    else update(tr[now].r,mid+1,r,x);
    tr[now].val=tr[tr[now].l].val+tr[tr[now].r].val;
} 
inline int merge(int a,int b,int l,int r){
    if(!a)return b;
    if(!b)return a;
    if(l==r){
        tr[a].val+=tr[b].val;
        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);
    tr[a].val=tr[tr[a].l].val+tr[tr[a].r].val;
    return a;
}
inline void dfs(int x,int fa){
    for(int i=beg[x];i;i=nex[i]){
        int t=to[i];
        if(t==fa)continue;
        dfs(t,x);
        merge(rt[x],rt[t],1,n);
    }
    ans[x]=query(rt[x],1,n,p[x],n);
    update(rt[x],1,n,p[x]);
}
int main(){
    n=read();
    for(int i=1;i<=n;i++){
        tmp[i]=p[i]=read();
        rt[i]=i;
    }
    sort(tmp+1,tmp+1+n);
    for(int i=1;i<=n;i++)
        p[i]=lower_bound(tmp+1,tmp+1+n,p[i])-tmp;
    int x;
    for(int i=2;i<=n;i++){
        x=read();
        add(i,x),add(x,i);
    }
    cnt=n;
    dfs(1,0);
    for(int i=1;i<=n;i++)
        printf("%d\n",ans[i]);
    return 0;
}

深深地感到自己的弱小。

posted @ 2020-03-27 22:02  syzf2222  阅读(162)  评论(0编辑  收藏  举报