题意:
一棵二叉树有n个叶子节点,每个叶子节点有权值,可以交换左右子树,问先序遍历这棵树,得到的最小逆序对数。
思路:
应该算是线段树合并的入门题。
暴力每个非叶子节点,用ans1记录不交换左右子树时,跨越左子树和右子树的答案,即左子树大于右子树的数的对数,ans2记录交换左右子树时的答案。
查询左子树中大于右子树的对数可以对左右子树各建一棵权值线段树,在线段树合并的过程中,左子树的线段树的右区间的值一定大于右子树的左区间的值,统计一下,就好了。
代码:
#include <bits/stdc++.h>
#define MID int mid=(l+r)>>1;
#define LL long long
using namespace std;
const int maxn=4e5+5;
int root[maxn], n, top, siz[maxn*2+1], a[maxn*2+1], ll[maxn*2+1], rr[maxn*2+1];
LL ans[maxn*2+1], ans1, ans2;
int lson[maxn*10], rson[maxn*10];
LL val[maxn*10];
int cnt;
int tot;
void read(int &x)
{
scanf("%d", &a[tot]);
x=tot;
siz[x]=1;
tot++;
if(a[x]==0)
{
read(ll[x]);read(rr[x]);
siz[x]+=siz[ll[x]]+siz[rr[x]];
}
return;
}
void insert(int &root, int l, int r, int x)
{
if(root==0)root=++cnt;
val[root]=1;
if(l==r)
{
return;
}
MID
lson[root]=rson[root]=0;
if(x<=mid)insert(lson[root], l, mid, x);
else insert(rson[root], mid+1, r, x);
}
int merg( int rtl, int rtr)
{
if(rtl==0 || rtr==0)return rtl|rtr;
ans1+=(val[rson[rtl]]*val[lson[rtr]]);
ans2+=(val[lson[rtl]]*val[rson[rtr]]);
val[rtl]+=val[rtr];
lson[rtl]=merg(lson[rtl], lson[rtr]);
rson[rtl]=merg(rson[rtl], rson[rtr]);
return rtl;
}
LL ANS;
void dfs(int x)
{
if(a[x])return;
if(siz[ll[x]]>siz[rr[x]]){dfs(ll[x]);dfs(rr[x]);}
else {dfs(rr[x]); dfs(ll[x]);}
// printf("%d %d\n", ll[x], rr[x]);
ans1=0, ans2=0;
root[x]=merg(root[ll[x]], root[rr[x]]);
ANS+=ans1<ans2?ans1:ans2;
return;
}
int main()
{
int i, j;
cin>>n;
tot=0;
read(top);
for(i=0; i<tot; i++)
{
if(a[i]!=0)insert(root[i], 1, n, a[i]);
}
dfs(top);
printf("%lld\n", ANS);
}