BZOJ2212: [Poi2011]Tree Rotations(洛谷P3521)

本文深入探讨了线段树合并算法,通过实例讲解如何在子树中进行数值的高效合并,采用权值线段树处理子区间数的计算,最小化合并成本,适用于BZOJ及洛谷平台的算法题解决。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

线段树合并

BZOJ题目传送门
洛谷题目传送门

学了发线段树合并。

对于一棵子树,无论它是否交换,都不会影响到这棵子树外的其它节点。那么我们只要每一次取小的那个就好了。

对每个节点开一棵权值线段树,每次把左右儿子合并上来。而左右儿子有两个贡献,分别为一个儿子左区间的数个数*另一个儿子右区间的数个数,对这两个取min即可。

代码:

#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 400005
#define F inline
using namespace std;
typedef long long LL;
struct tree{ int l,r; LL s; }t[N<<4];
int n,m,nd,a[N],to[N][2],rt[N];
LL ans,L,R;
F char readc(){
	static char buf[100000],*l=buf,*r=buf;
	if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
	return l==r?EOF:*l++;
}
F int _read(){
	int x=0; char ch=readc();
	while (!isdigit(ch)) ch=readc();
	while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=readc();
	return x;
}
void dfs1(int &x){
	if (!(a[x=++nd]=_read()))
		dfs1(to[x][0]),dfs1(to[x][1]);
}
F void pshp(int x){ t[x].s=t[t[x].l].s+t[t[x].r].s; }
void mdfy(int &x,int l,int r,int p){
	x=++nd; int mid=l+r>>1;
	if (l==r) return void(t[x].s=1);
	if (p<=mid) mdfy(t[x].l,l,mid,p);
	else mdfy(t[x].r,mid+1,r,p); pshp(x);
}
int mrg(int x,int y){
	if (!x||!y) return x+y;
	L+=t[t[x].r].s*t[t[y].l].s;
	R+=t[t[x].l].s*t[t[y].r].s;
	t[x].l=mrg(t[x].l,t[y].l);
	t[x].r=mrg(t[x].r,t[y].r);
	return pshp(x),x;
}
LL dfs(int x){
	LL ret=0;
	if (!a[x]){
		ret=dfs(to[x][0])+dfs(to[x][1]),L=R=0;
		rt[x]=mrg(rt[to[x][0]],rt[to[x][1]]),ret+=min(L,R);
	}
	else mdfy(rt[x],1,n,a[x]); return ret;
}
int main(){ return n=_read(),dfs1(m),nd=0,printf("%lld\n",dfs(1)),0; }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值