https://www.lydsy.com/JudgeOnline/problem.php?id=2212
线段树合并核心代码就是merge 学过线段树一看就明白了
这道题虽然是裸题 但不看题解的话题目本身并不那么简单 给一棵二叉树 所有叶节点都有个权值可以随意交换任一非叶节点的左右子孩子 来使最终的逆序数最小
并未限制交换次数 所以每棵子树分开考虑就好 一开始对每个节点都建立一棵线段树 但其实只有一条链 所以像主席树那样动态分配节点即可 然后在递归二叉树时对每棵子树 将其左右两棵子树建立的权值线段树合并 在线段树合并的递归过程中即可求出对于当前子树转或不转的代价
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=5e6+10;
ll res1,res2,ans;
int ch[maxn][2],rot[maxn],val[maxn],lef[maxn],rgt[maxn];
int root;
int n,num1,num2;
void pushup(int cur)
{
val[cur]=val[lef[cur]]+val[rgt[cur]];
}
void update(int &cur,int tar,int l,int r)
{
int m;
if(cur==0) cur=++num2;
if(l==r){
val[cur]=1;
return;
}
m=(l+r)/2;
if(tar<=m) update(lef[cur],tar,l,m);
else update(rgt[cur],tar,m+1,r);
pushup(cur);
}
void dfs(int &cur)
{
int val;
cur=++num1;
scanf("%d",&val);
if(val==0) dfs(ch[cur][0]),dfs(ch[cur][1]);
else update(rot[cur],val,1,n);
}
int unite(int x,int y)
{
if(x==0) return y;
if(y==0) return x;
res1+=(ll)(val[lef[x]])*(ll)(val[rgt[y]]);
res2+=(ll)(val[lef[y]])*(ll)(val[rgt[x]]);
lef[x]=unite(lef[x],lef[y]);
rgt[x]=unite(rgt[x],rgt[y]);
pushup(x);
return x;
}
void solve(int cur)
{
if(ch[cur][0]==0) return;
solve(ch[cur][0]),solve(ch[cur][1]);
res1=0,res2=0;
rot[cur]=unite(rot[ch[cur][0]],rot[ch[cur][1]]);
ans+=min(res1,res2);
}
int main()
{
int i;
scanf("%d",&n);
dfs(root);
solve(root);
printf("%lld\n",ans);
return 0;
}