线段树合并部分总结

昨天看了半天动态开点,写了主席树复习笔记,今天来搞搞线段树合并,也回应一下昨天的话

线段树合并总结与题目处理

注释:按照惯例,本篇为复习笔记,对于对动态开点,甚至是merge的中文意思都不知道的dalao,请自动回溯,我帮你(return);

首先吧,作用类分析QAQ

  1. 线段树合并一般用于对子树的统计,一般的套路就是对树的每一个节点都开上一颗动态开点线段树,然后统计子树信息时,合并所有儿子信息,统计答案,然后继续向上走;

其实本质上就是把两棵线段树合起来,所以合并合并,怎么合是个问题QWQQWQ;
如果是原型的线段树,对于合并后的树操作会。。发现之前的状态全没了。。而线段树合并问题一般都为状态持久化问题。。或者就是在树上处理左右子树,然后在合并左右子树的一个过程,比如刷线段树合并必刷的这题T1后面会给题解,如果后期有时间会写思路

然后吧,建树过程QWQ

  1. 昨天好像说过了吧。。和主席树一模一样,具体可以参考我的主席树博客。。主席树
    首先在这里不再推荐build操作,当然如果你想,我不会拦你(大佬自动忽略)
int build(int l,int r){
   int now=++cnt; sum[now]=0;
   if (l<r) {
   	L[now]=build(l,mid);
   	R[now]=build(mid+1,r);
   }
   return now;
}

这里如果用build的话,,怎么说呢,,,没问题,,,总之,,,,我就是不喜欢用QWQQWQ
然后继续套树

int change(int l,int r,int las,int t){
	int now=++cnt; sum[now]=sum[las]+1;
	L[now]=L[las]; R[now]=R[las]; 
	if (l<r){
		if (t<=mid) L[now]=change(l,mid,L[las],t);
		else R[now]=change(mid+1,r,R[las],t);
	}
	return now;
}

这里的放数,基本模板依然相同。。不对,,,要改改,就是这里一般是初始化操作,,,
一般在代码中是这样修改数据就是直接__++__操作。 就OK了。。

下面我在放放我最常用的数组型线段树建立
贴下代码

void update(int &now,int l,int r,int pla){
    if (!now) now=++cnt; tree[now].num++;
    if (l==r) return;
    if (pla<=mid) update(tree[now].lc,l,mid,pla);else update(tree[now].rc,mid+1,r,pla);
}

其实和主席树里的,,一模一样。。我是真没话说了。。

下面呢,才是最重要的,,MERGE,,中文线段树合并QJQ

首先这是一般模板

int rebuild(int x,int y){
	if(x==y) return x;
	if (x==0) return y;
	if (y==0) return x;
	sum[x]+=sum[y]; l[x]=rebuild(l[x],l[y]); r[x]=rebuild(r[x],r[y]);
	if (!r[x]) fa[x]=fa[l[x]]; else fa[x]=fa[r[x]];
	return x;
}

可以简化一下就是将函数的int类型改为void类型

void rebuild(int &x,int y){
	if(x==y) return;
	if (!x||!y) {x+=y; return; } 
	sum[x]+=sum[y]; rebuild(l[x],l[y]); rebuild(r[x],r[y]);
	if (!r[x]) fa[x]=fa[l[x]]; else fa[x]=fa[r[x]];
}

这里主要思路就是把右子树全部并到左子树上并返回合并后树的编号。。就这样。。
当然模板终究是模板,这里就有特例
是T1就是上面提到的,,,

void merge(int &lc,int rc){
    if (!lc||!rc) {lc+=rc; return;}
    tree[lc].num+=tree[rc].num;
    ans1+=1ll*tree[tree[lc].rc].num*tree[tree[rc].lc].num;
    ans2+=1ll*tree[tree[lc].lc].num*tree[tree[rc].rc].num;
    merge(tree[lc].lc,tree[rc].lc); merge(tree[lc].rc,tree[rc].rc);}

可以看到。。中间多了一些东西,那是应题目需要搞得东西,,所以不能只背模板哎哎哎。
但是总体上都是相同的,八九不离十。。恩恩就这样

当然只有少数题目会想这题可以一边合并一边统计答案并输出,,,

所以

搜树 ?QUQ

int search(int x,int y){
	if (sum[x]<y) return -1; 
	if (sum[x]==y) return fa[x];
	if (sum[l[x]]<y) return search(r[x],y-sum[l[x]]);
	else return search(l[x],y);  
}

熟悉不,熟悉不,
不熟悉?请出门左转不送,我帮你 return(order) <order==go out then turn left>
这就是正常的主席树查询。。。正常到不想讲,,,,

书写答案,请把你的答案工工整整的用cout/printf/write()/io.write 输出来吧!!QYQ


下面是题目时间,有时间来补坑,,,

  1. 永无乡
    总体思路为爆搜,爆搜,,,你说可能吗。。。。

是冰茶几+线段树合并。了啦

#include<bits/stdc++.h>
using namespace std;
int n,m,cnt,w[2000000],sum[2000000],l[5000000],r[2000000],fa[2300000],fid[2000000];
void add(int xxx,int ll,int rr,int xx,int yy){
	if (ll==rr) {
		sum[xxx]++; 
		fa[xxx]=yy;
		return; 
	}
	int mid=(ll+rr)>>1; 
	if (xx<=mid) {
	if (!l[xxx])l[xxx]=++cnt;
		add(l[xxx],ll,mid,xx,yy);
	}
	else {
	if (!r[xxx]) r[xxx]=++cnt;
		add(r[xxx],mid+1,rr,xx,yy);}
	sum[xxx]=sum[l[xxx]]+sum[r[xxx]]; 
	if (!r[xxx]) fa[xxx]=fa[l[xxx]]; else fa[xxx]=fa[r[xxx]];
}
int find(int x){
	return (x==fid[x])?x:fid[x]=find(fid[x]);}
int rebuild(int x,int y){
	if(x==y) return x;
	if (x==0) return y;
	if (y==0) return x;
	sum[x]+=sum[y]; l[x]=rebuild(l[x],l[y]); r[x]=rebuild(r[x],r[y]);
	if (!r[x]) fa[x]=fa[l[x]]; else fa[x]=fa[r[x]];
	return x;
}
int search(int x,int y){
	if (sum[x]<y) return -1; 
	if (sum[x]==y) return fa[x];
	if (sum[l[x]]<y) return search(r[x],y-sum[l[x]]);
	else return search(l[x],y);  
}
int main(){
	cin>>n>>m; cnt=n;
	for(int i=1; i<=n; ++i) cin>>w[i],add(i,1,n,w[i],i),fid[i]=i; 	
	for(int i=1; i<=m; ++i) {
		int x,y;  cin>>x>>y; x=find(x); y=find(y); fid[x]=fid[y]=rebuild(x,y);}
	int T;cin>>T;
	while (T--) {
		char c; cin>>c; if (c=='Q') {
			int x,y; cin>>x>>y;x=find(x); cout<<search(x,y)<<endl;}
		else {
			int x,y; cin>>x>>y; x=find(x); y=find(y); fid[x]=fid[y]=rebuild(x,y); 
		}
	}
}

  1. P3521 [POI2011]ROT-Tree Rotations
    这题就是就是逆序对思想,有点巧妙。。还有线段树合并
#include<bits/stdc++.h>
#define REP(i,a,b) for(int i(a);i<=(b);++i)
#define mid ((l+r)>>1)
using namespace std;
long long smin(long long  x,long long y){return (x>y)?y:x;}
int n,cnt; long long ans1,ans2,ans;
struct node{int num,lc,rc;}tree[15000010];
void read(int &x){
	x=0; char c=getchar(); int f=1; for(;!isdigit(c);c=getchar()) if (c=='-') f=-f;
	for(;isdigit(c);c=getchar()) x=x*10+c-'0'; x*=f;}
void pushup(int now){
    tree[now].num=tree[tree[now].lc].num+tree[tree[now].rc].num;}
void update(int &now,int l,int r,int pla){
    if (!now) now=++cnt; tree[now].num++;
    if (l==r) return;
    if (pla<=mid) update(tree[now].lc,l,mid,pla);else update(tree[now].rc,mid+1,r,pla);
}
void merge(int &lc,int rc){
    if (!lc||!rc) {lc+=rc; return;}
    tree[lc].num+=tree[rc].num;
    ans1+=1ll*tree[tree[lc].rc].num*tree[tree[rc].lc].num;
    ans2+=1ll*tree[tree[lc].lc].num*tree[tree[rc].rc].num;
    merge(tree[lc].lc,tree[rc].lc); merge(tree[lc].rc,tree[rc].rc);}
void dfs(int &x){ 
    int now; x=0; read(now); int lc=0,rc=0;
    if (!now){
        dfs(lc); dfs(rc);  ans1=0,ans2=0; 
        x=lc,merge(x,rc); ans+=smin(ans1,ans2);}
    else update(x,1,n,now); }
void solve(){ int hehe=0;
    read(n);dfs(hehe); printf("%lld",ans);}
int main(){
    solve();} 

P3605 [USACO17JAN]Promotion Counting晋升者计数

#include<bits/stdc++.h>
#define REP(i,a,b) for(register int i(a);i<=(b);++i)
#define mid ((l+r)>>1)
#define ll(a) (a<<1)
#define rr(a) (a<<1|1)
using namespace std;
int n,m,b[1000000],Ans[2000000],cnt,head[1000000],tre[4000000];
struct node{int num,pla;}a[4000000];
bool cmp(node a,node b) {return a.num<b.num;}
struct mode{int head;int next;}tree[5000000];
void addeag(int x,int y){++cnt;tree[cnt].head=head[x]; tree[cnt].next=y;head[x]=cnt;}
void rebuild(int xx,int l,int r,int now){
	if (l>xx||r<xx) return; if (l==xx&&r==xx) {tre[now]++; return; }
	rebuild(xx,l,mid,(now<<1));rebuild(xx,mid+1,r,(now<<1|1)); 
	tre[now]=tre[(now<<1)]+tre[(now<<1|1)];}
int search(int xx,int yy,int l,int r,int now){
	if (l>yy||r<xx) return 0; 
	if (l>=xx&&r<=yy) return tre[now]; int ans=0;  //cout<<now<<endl;
	ans+=search(xx,yy,l,mid,(now<<1)); ans+=search(xx,yy,mid+1,r,(now<<1|1));return ans;}
void let_dfs(int x){
	int FINAL=search(b[x]+1,n,1,n,1); 
	for(int i=head[x];i;i=tree[i].head){
		int y=tree[i].next; let_dfs(y); } 
	Ans[x]=search(b[x]+1,n,1,n,1)-FINAL;
	rebuild(b[x],1,n,1);}
int main(){
	std::ios::sync_with_stdio(false);cin>>n;
	for(int i=1; i<=n; ++i) cin>>a[i].num,a[i].pla=i;
	sort(a,a+n+1,cmp); for(int i=1; i<=n; ++i) b[a[i].pla]=i;
	REP(i,2,n) {int x;cin>>x;addeag(x,i);}
	let_dfs(1);REP(i,1,n) cout<<Ans[i]<<endl;
}

好了就这样
· THE END

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值