线段树合并:从入门到放弃
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;
}
例题2 洛谷P4556雨天的尾巴
- 例题3 洛谷P3224永无乡
例题4 NOIP2016 天天爱跑步
题目先咕着,有空写。
以上
以上是关于线段树合并:从入门到放弃的主要内容,如果未能解决你的问题,请参考以下文章