博客园 首页 私信博主 显示目录 隐藏目录 管理 动画

BZOJ.2212.[POI2011]Tree Rotations(线段树合并)

题目链接


Description

给定一棵n个叶子的二叉树,每个叶节点有权值(1ain)。可以任意的交换两棵子树。求最后顺序遍历树得到的叶子权值序列中,最少的逆序对数是多少。

Solution

很重要的一点是在子树内部交换左右儿子对其它子树是没有影响的。(当然更大区间内交换两棵子树对子树内部也是没有影响的)
所以DFS,对每个节点的两棵子树,如果换了更优就换,不优就不换。
怎么统计两棵子树换/不换产生的逆序对数呢,用两棵子树的值域线段树合并解决。换/不换产生的逆序对数根据子树的大小关系判断就行了。
时间空间都是O(nlogn).

在这里记一下我个人对线段树合并复杂度的感性证明吧...(好像就是势能分析)

每次合并两棵树,代价是两棵树的公共节点数,设它是x
在合并完两棵树后,这两棵树的2x个公共节点被合并成了x个,相当于删掉了x个点。
所以合并的代价(复杂度)就是,被合并点的点的个数,也就是删掉的点的个数。
而要删掉这个点就要先存在这个点,初始一共有nlogn个节点,所以删掉点的个数不会超过nlogn,所以总复杂度不会超过nlogn

如果初始是对每个节点进行一次区间修改,和插入单点一样只会影响logn个点,所以初始还是一共最多有nlogn个点,复杂度一样。

另外复杂度也不完全是公共节点数,因为还要从它往下一层才知道它是公共节点。
也许是因为这个能卡一些线段树合并的复杂度吧,但是影响不大不管了

//80628kb	7088ms
#include <cstdio>
#include <cctype>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 300000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=2e5+5;

int n;
LL Ans;
char IN[MAXIN],*SS=IN,*TT=IN;
struct Segment_Tree
{
	#define S N*19//只有建树、合并的话 nlogn就够了 n(logn+1)!
	#define lson son[x][0]
	#define rson son[x][1]
	int tot,sz[S],son[S][2];

	void Insert(int &x,int l,int r,int p)
	{
		sz[x=++tot]=1;
		if(l==r) return;
		int m=l+r>>1;
		if(p<=m) Insert(lson,l,m,p);
		else Insert(rson,m+1,r,p);
	}
	int Merge(int x,int y,LL &ans1,LL &ans2)
	{
		if(!x||!y) return x^y;
		ans1+=1ll*sz[rson]*sz[son[y][0]], ans2+=1ll*sz[lson]*sz[son[y][1]];
		lson=Merge(lson,son[y][0],ans1,ans2);
		rson=Merge(rson,son[y][1],ans1,ans2);
		sz[x]+=sz[y];// sz[x]=sz[lson]+sz[rson]; 这种写法在合并叶子节点时不对啊!(y更新不了x)
		return x;
	}
//	void Print(int x,int l,int r)
//	{
//		if(!x) return;
//		printf("%d:%d~%d sz:%d\n",x,l,r,sz[x]);
//		if(l==r) ;
//		else Print(lson,l,l+r>>1), Print(rson,(l+r>>1)+1,r);
//	}
}T;

inline int read()
{
	int now=0;register char c=gc();
	for(;!isdigit(c);c=gc());
	for(;isdigit(c);now=now*10+c-'0',c=gc());
	return now;
}
int DFS()//返回root 
{
	int v=read();
	if(v) {int x; T.Insert(x,1,n,v); return x;}
	LL ans1=0, ans2=0;
	int rt=T.Merge(DFS(),DFS(),ans1,ans2);//当然参数顺序是反着的 
	Ans+=std::min(ans1,ans2);
	return rt;
}

int main()
{
	n=read(), DFS(), printf("%lld\n",Ans);
	return 0;
}
posted @   SovietPower  阅读(1341)  评论(1编辑  收藏  举报
编辑推荐:
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· C# 深度学习:对抗生成网络(GAN)训练头像生成模型
· .NET 适配 HarmonyOS 进展
阅读排行:
· 手把手教你更优雅的享受 DeepSeek
· AI工具推荐:领先的开源 AI 代码助手——Continue
· 探秘Transformer系列之(2)---总体架构
· V-Control:一个基于 .NET MAUI 的开箱即用的UI组件库
· 乌龟冬眠箱湿度监控系统和AI辅助建议功能的实现
点击右上角即可分享
微信分享提示