【bzoj3702】二叉树
来源:互联网 发布:sql server介绍 编辑:程序博客网 时间:2024/06/18 15:13
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。
题解
线段树合并。
我们发现交换两个儿子,则每个儿子原有的逆序对数量不会改变。
于是我们自底向上贪心,每次使节点的逆序对数量尽量少。
对于每个点开权值线段树,代表有哪几个权值的点,合并左右儿子,利用合并的性质判断是否交换。
代码
#include<bits/stdc++.h>#define ll long long#define inf 1000000000using namespace std;const int N=200005;const int mod=1000000007;inline int read(){ int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f;}ll ans,a,b;int n,sz,tot,v[N<<1],rt[N<<1],l[N<<1],r[N<<1];int t[N*20],ls[N*20],rs[N*20];void tree(int x){ v[x]=read(); if (v[x]==0) { l[x]=++sz; tree(l[x]); r[x]=++sz; tree(r[x]); }}void build(int &k,int l,int r,int x){ if (!k) k=++tot;t[k]++; if (l==r) return; int mid=(l+r)>>1; if (x<=mid) build(ls[k],l,mid,x);else build(rs[k],mid+1,r,x);}int merge(int x,int y){ if (!x) return y; if (!y) return x; a+=(ll)t[ls[x]]*t[rs[y]]; b+=(ll)t[ls[y]]*t[rs[x]]; ls[x]=merge(ls[x],ls[y]); rs[x]=merge(rs[x],rs[y]); t[x]=t[ls[x]]+t[rs[x]]; return x;}void solve(int x){ if (!x) return; solve(l[x]);solve(r[x]); if (!v[x]) { a=b=0; rt[x]=merge(rt[l[x]],rt[r[x]]); ans+=min(a,b); }}int main(){ n=read(); sz=1;tree(1); for (int i=1;i<=sz;i++) if (v[i]) build(rt[i],1,n,v[i]); solve(1); cout<<ans; return 0;}
阅读全文