1. 程式人生 > >bzoj 3702: 二叉樹 (線段樹)

bzoj 3702: 二叉樹 (線段樹)

3702: 二叉樹

Time Limit: 15 Sec  Memory Limit: 256 MB
Submit: 428  Solved: 184
[Submit][Status][Discuss]

Description

現在有一棵二叉樹,所有非葉子節點都有兩個孩子。在每個葉子節點上有一個權值(有n個葉子節點,滿足這些權值為1..n的一個排列)。可以任意交換每個非葉子節點的左右孩子。
要求進行一系列交換,使得最終所有葉子節點的權值按照中序遍歷寫出來,逆序對個數最少。

Input

第一行n
下面每行,一個數x
如果x==0,表示這個節點非葉子節點,遞迴地向下讀入其左孩子和右孩子的資訊,
如果x!=0,表示這個節點是葉子節點,權值為x。

Output

一行,最少逆序對個數。

Sample Input

3
0
0
3
1
2

Sample Output

1

HINT

對於100%的資料:2<=n<=200000。

Source



題解:同bzoj 2212

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 400003
#define LL long long
using namespace std;
int n,m,val[N],rt,ls[N*20],rs[N*20],tot;
int l[N],r[N],maxn,num[N],cnt,in[N],out[N],q[N],sz,root[N];
LL sl,sr,sum[N*20];
int dfs()
{
	int now=++cnt;
	scanf("%d",&val[now]);
	if (val[now]) {
		q[++sz]=val[now];
		in[now]=out[now]=sz;
		return now;
	}
	in[now]=sz+1;
	l[now]=dfs(); r[now]=dfs();
	out[now]=sz;
	return now;
}
void update(int x)
{
	sum[x]=sum[ls[x]]+sum[rs[x]];
}
void insert(int &i,int l,int r,int x)
{
	i=++tot;
	if (l==r) {
		sum[i]=1; 
		return;
	}
	int mid=(l+r)/2;
	if (x<=mid) insert(ls[i],l,mid,x);
	else insert(rs[i],mid+1,r,x);
	update(i);
}
int merge(int x,int y)
{
	if (!x) return y;
	if (!y) return x;
	sl+=sum[rs[x]]*sum[ls[y]];
	sr+=sum[ls[x]]*sum[rs[y]];
	ls[x]=merge(ls[x],ls[y]);
	rs[x]=merge(rs[x],rs[y]);
	update(x);
	return x;
}
LL solve(int x)
{
	if (val[x]) return 0;
	LL ans=solve(l[x])+solve(r[x]);
	sl=0; sr=0;
	root[x]=merge(root[l[x]],root[r[x]]);
	return ans+min(sl,sr);
}
int main()
{
	freopen("a.in","r",stdin);
	freopen("my.out","w",stdout);
	scanf("%d",&n);
	rt=dfs();
	for (int i=1;i<=cnt;i++)
	 if (val[i]) insert(root[i],1,n,val[i]);
    printf("%I64d\n",solve(rt));
}