【BZOJ2212】[POI2011]Tree Rotations (线段树合并)

【BZOJ2212】[POI2011]Tree Rotations (线段树合并)

题面

BZOJ
洛谷

题解

因为是一棵二叉树,我们发现对于左右儿子而言只有两种放法。
不考虑左右儿子内部的相对顺序,那么发现两个儿子先后顺序的逆序对数是固定的,而确定好顺序之后显然就是一个分治的过程。
那么显然每次决策都是选择放在前面后,左右之间贡献的逆序对数较少的那一边。
考虑这个过程如何实现,从上往下做显然不好做。
考虑从下往上合并,显然启发式合并是可行的,但是这样复杂度会多出一个\(log\)。我们发现可以在线段树合并的过程中维护逆序对,这样子复杂度就变成了一个\(log\)

#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long
#define MAX 400200
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
struct Node{int ls,rs,v;}t[MAX<<5];
int rt[MAX],tot;
void Modify(int &x,int l,int r,int p)
{
	if(!x)x=++tot;t[x].v+=1;if(l==r)return;
	int mid=(l+r)>>1;
	if(p<=mid)Modify(t[x].ls,l,mid,p);
	else Modify(t[x].rs,mid+1,r,p);
}
ll tao1,tao2,ans;
void Merge(int &x,int y)
{
	if(!x||!y){x|=y;return;}
	t[x].v+=t[y].v;
	tao1+=1ll*t[t[x].ls].v*t[t[y].rs].v;
	tao2+=1ll*t[t[x].rs].v*t[t[y].ls].v;
	Merge(t[x].ls,t[y].ls);
	Merge(t[x].rs,t[y].rs);
}
int node=0,n;
int ch[MAX][2],V[MAX];
int gettree()
{
	int x=read(),u=++node;
	if(!x)ch[u][0]=gettree(),ch[u][1]=gettree();
	else V[u]=x;
	return u;
}
void dfs(int u)
{
	if(V[u]){Modify(rt[u],1,n,V[u]);return;}
	dfs(ch[u][0]);dfs(ch[u][1]);tao1=tao2=0;
	Merge(rt[u]=rt[ch[u][0]],rt[ch[u][1]]);
	ans+=min(tao1,tao2);
}
int main()
{
	n=read();gettree();
	dfs(1);
	printf("%lld\n",ans);
	return 0;
}
posted @ 2018-11-01 14:39  小蒟蒻yyb  阅读(319)  评论(0编辑  收藏  举报