bzoj 3702: 二叉树 线段树合并

来源:互联网 发布:看广告赚钱软件 编辑:程序博客网 时间:2024/05/22 12:08

题意

现在有一棵二叉树,所有非叶子节点都有两个孩子。在每个叶子节点上有一个权值(有n个叶子节点,满足这些权值为1..n的一个排列)。可以任意交换每个非叶子节点的左右孩子。
要求进行一系列交换,使得最终所有叶子节点的权值按照中序遍历写出来,逆序对个数最少。
n<=200000

分析

自底向上,在线段树合并的时候顺便计算逆序对和顺序对的数量,取较小的一个即可。

代码

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>using namespace std;typedef long long LL;const int N=200005;int n,val[N*2],sz,l[N*2],r[N*2],root[N*2],cnt;LL s1,s2,ans;struct tree{int l,r,s;}t[N*20];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;}void init(int &x){    int w=read();x=++sz;    if (!w) val[x]=w,init(l[x]),init(r[x]);    else val[x]=w;}void ins(int &d,int l,int r,int x){    if (!d) d=++sz;    t[d].s++;    if (l==r) return;    int mid=(l+r)/2;    if (x<=mid) ins(t[d].l,l,mid,x);    else ins(t[d].r,mid+1,r,x);}int merge(int x,int y){    if (!x||!y) return x^y;    s1+=(LL)t[t[x].r].s*t[t[y].l].s;    s2+=(LL)t[t[x].l].s*t[t[y].r].s;    t[x].s+=t[y].s;    t[x].l=merge(t[x].l,t[y].l);    t[x].r=merge(t[x].r,t[y].r);    return x;}void dfs(int x){    if (val[x]) ins(root[x],1,n,val[x]);    else    {        dfs(l[x]);dfs(r[x]);        s1=0;s2=0;        root[x]=merge(root[l[x]],root[r[x]]);        ans+=min(s1,s2);    }}int main(){    n=read();    int rt;sz=0;    init(rt);    sz=0;    dfs(rt);    printf("%lld",ans);    return 0;}
原创粉丝点击