【BZOJ】2212: [Poi2011]Tree Rotations

题意

给一棵\(n(1 \le n \le 200000)\)个叶子的二叉树,可以交换每个点的左右子树,要求前序遍历叶子的逆序对最少。

分析

可以发现如果交换非叶结点的左右子树,对子树内的交换无影响,对子树外的交换也无影响,所以答案的贡献只是左子树与右子树之间是否交换得到的最小的逆序对数。

题解

考虑分治,对于一个点\(x\),我们只需要将其其中的一个子树的叶子插入到bit中,然后用另一个子树的叶子就能求得其逆序对数。那么发现一个点在遍历过程中可能插入的次数不只1次,对复杂度的影响主要就是重复插入的那些点(否则复杂度是\(O(nlogn)\)的)。由于无论是将哪棵子树插入到bit中都能求得其答案,所以我们贪心的将叶子数最小的那棵子树插入到bit中即可。这样插入到\(bit\)的次数总共不超过\(n\)次。所以复杂度是\(O(nlogn)\)的。

#include <bits/stdc++.h>
using namespace std;
inline int getint() {
	int x=0, c=getchar();
	for(; c<48||c>57; c=getchar());
	for(; c>47&&c<58; x=x*10+c-48, c=getchar());
	return x;
}
const int N=200005, M=N*3;
typedef long long ll;
int a[N], b[M][2], c[M][2], d[N], now, tot, s[M], n;
ll ans;
void upd(int x, int g) {
	for(; x<=n; x+=x&-x) {
		d[x]+=g;
	}
}
int sum(int x) {
	int y=0;
	for(; x; x-=x&-x) {
		y+=d[x];
	}
	return y;
}
void dfs1(int x) {
	int w=getint();
	if(w) {
		a[++tot]=w;
		c[x][0]=-w;
		b[x][0]=tot;
		b[x][1]=tot;
		s[x]=1;
		return;
	}
	int l, r;
	dfs1(c[x][0]=l=++now);
	dfs1(c[x][1]=r=++now);
	b[x][0]=b[l][0];
	b[x][1]=b[r][1];
	s[x]=s[l]+s[r];
}
void dfs2(int x) {
	if(s[x]==1) {
		upd(-c[x][0], 1);
		return;
	}
	int ch=s[c[x][1]]<s[c[x][0]], l=c[x][ch], r=c[x][ch^1];
	dfs2(l);
	for(int i=b[l][0], g=b[l][1]; i<=g; upd(a[i++], -1));
	dfs2(r);
	ll mn=0, mx=0;
	for(int i=b[l][0], g=b[l][1]; i<=g; ++i) {
		int t=sum(a[i]-1);
		mn+=t;
		mx+=s[r]-t;
	}
	ans+=min(mn, mx);
	for(int i=b[l][0], g=b[l][1]; i<=g; upd(a[i++], 1));
}
int main() {
	n=getint();
	dfs1(now=1);
	dfs2(1);
	printf("%lld\n", ans);
	return 0;
}
posted @ 2015-11-22 13:39  iwtwiioi  阅读(273)  评论(0编辑  收藏  举报