题目链接:https://cn.vjudge.net/contest/287725#problem/B
题目大意:现在有一棵二叉树,所有非叶子节点都有两个孩子。在每个叶子节点上有一个权值(有n个叶子节点,满足这些权值为1..n的一个排列)。可以任意交换每个非叶子节点的左右孩子。
要求进行一系列交换,使得最终所有叶子节点的权值按照遍历序写出来,逆序对个数最少。
具体思路:首先说一下对逆序对的处理。2431的逆序数是4((2,1),(4,3),(4,3),(3,1))。但是两个for循环处理肯定是不行的,我们可以通过线段树来求出逆序对数,每插入一个数,就先判断一个这个数在线段树上后面有多少个数就可以了。
第一次插入2,后面没有数,此时逆序对数为0。
第二次插入4,后面没有数,此时的逆序对数为0.
第三次插入3,后面有2这个数,此时的逆序对数为1。
第四次插入1,后米娜有2,3,4这三个数,此时的逆序对数为1+3=4。
然后再就是对于题目中所说的交换操作了。在线段树中每一个节点的逆序对数等于这个节点的左孩子的逆序对数+右孩子的逆序对数+后面的树中的逆序对数
如果是交换当期节点的左右孩子,当前这个点的逆序对数等于左孩子中大于等于当当前节点值的个数*右孩子中小于等于当前节点值的个数。
如果不交换的话,当前这个点的逆序对数等于左孩子中小于等于当前节点值的个数*右孩子中小于等于当前节点值的个数。
ch数组代表的是当前这个节点在初始二叉树中左孩子和右孩子的编号。
cont代表的数当前在线段树上的编号的贡献
le代表的是当前节点左孩子在线段树的编号
ri代表的是当前节点右孩子在线段树的编号
AC代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 # define ll long long 4 const int maxn = 4e6+100; 5 const int mod =1e6; 6 # define LL_inf 0x3f3f3f3f3f3f3f 7 ll n,root,ans,t1,t2; 8 ll num1,num2; 9 ll ch[maxn][2]; 10 ll cont[maxn]; 11 ll le[maxn],ri[maxn],father[maxn]; 12 void up(ll x){ 13 cont[x]=cont[le[x]]+cont[ri[x]]; 14 } 15 void update(ll &x,ll val,ll l,ll r){ 16 if(!x)x=++num2;//如果之前出现过就不需要在新建节点了 17 if(l==r){ 18 cont[x]=1; 19 return ; 20 } 21 ll mid=l+r>>1; 22 if(val<=mid)update(le[x],val,l,mid); 23 else update(ri[x],val,mid+1,r); 24 up(x); 25 } 26 void dfs(ll &x){ 27 x=++num1; 28 ll tmp; 29 scanf("%lld",&tmp); 30 if(!tmp){ 31 dfs(ch[x][0]); 32 dfs(ch[x][1]); 33 } 34 else update(father[x],tmp,1,n); 35 } 36 ll emerge(ll u,ll v){ 37 if(!u)return v; 38 if(!v)return u; 39 t1+=(cont[le[u]]*cont[ri[v]]); 40 t2+=(cont[le[v]]*cont[ri[u]]); 41 le[u]=emerge(le[u],le[v]); 42 ri[u]=emerge(ri[u],ri[v]); 43 up(u); 44 return u; 45 } 46 void solve(ll x){ 47 if(!ch[x][0])return ; 48 solve(ch[x][0]); 49 solve(ch[x][1]); 50 t1=0,t2=0; 51 father[x]=emerge(father[ch[x][0]],father[ch[x][1]]);//注意这个是father 52 ans+=min(t1,t2); 53 } 54 int main(){ 55 scanf("%lld",&n); 56 dfs(root); 57 solve(root); 58 printf("%lld ",ans); 59 }