输入格式真的毒瘤
权值线段树合并。
我们先对每一个叶子建一棵权值线段树,并把它自己的权值插入到里面。
我们不妨设原树中当前节点为 u u u,爸爸 f a fa fa,左儿子 l c lc lc,右儿子 r c rc rc,那么显然这棵树中的逆序对分为 3 3 3 个部分: l c lc lc 里的逆序对, r c rc rc 里的逆序对和跨 l c lc lc 和 r c rc rc 的逆序对。
而我们现在已经把 l c lc lc 里的逆序对和 r c rc rc 里的逆序对算好了,现在需要求出跨 l c lc lc 和 r c rc rc 的逆序对,也就是合并 l c lc lc 和 r c rc rc 。然后我们发现,无论 l c lc lc 内的子树怎么换来换去, r c rc rc内的子树怎么换来换去,都是对跨 l c lc lc 和 r c rc rc 的逆序对个数没有影响的。
同理,无论我们交不交换 l c lc lc 和 r c rc rc,对 f a fa fa 那一层的合并也是没有影响的。
所以对于跨 l c lc lc 和 r c rc rc 的逆序对,我们分交换 l c lc lc、 r c rc rc 和不交换的情况算出两种情况的逆序对个数,再取较小值就好了。
我们遍历两棵权值线段树来暴力合并两棵权值线段树。
那么对于 l c lc lc 和 r c rc rc 权值线段树中同一位置的点 a a a、 b b b,若不交换 l c lc lc 和 r c rc rc,显然 l c lc lc 中的点的编号(这里的编号是指前序遍历的先后顺序)肯定都在 r c rc rc 前, a a a 右儿子的权值必定大于 b b b 左儿子的权值,所以 s i z e [ c h [ a ] [ 1 ] ] ∗ s i z e [ c h [ b ] [ 0 ] ] size[ch[a][1]]*size[ch[b][0]] size[ch[a][1]]∗size[ch[b][0]] 就是这一位置的答案了。
同理,如果交换 l c lc lc 和 r c rc rc,这一位置的答案就是 s i z e [ c h [ a ] [ 0 ] ] ∗ s i z e [ c h [ b ] [ 1 ] ] size[ch[a][0]]*size[ch[b][1]] size[ch[a][0]]∗size[ch[b][1]]。
最后合并完统计答案即可。
代码如下:
#include<bits/stdc++.h>
#define N 200010
#define int long long
using namespace std;
struct Tree
{
int ch[2],size;
}t[N<<5];
int n,tot,ans,num1,num2;
int update(int l,int r,int val)
{
int u=++tot;
t[u].size=1;
if(l==r) return u;
int mid=(l+r)>>1;
if(val<=mid) t[u].ch[0]=update(l,mid,val);
else t[u].ch[1]=update(mid+1,r,val);
return u;
}
int merge(int a,int b,int l,int r)//把b合并至a
{
if(!a||!b) return a+b;
if(l==r)
{
t[a].size+=t[b].size;
return a;
}
num1+=t[t[a].ch[1]].size*t[t[b].ch[0]].size;//不交换的答案
num2+=t[t[b].ch[1]].size*t[t[a].ch[0]].size;//交换后的答案
int mid=(l+r)>>1;
t[a].ch[0]=merge(t[a].ch[0],t[b].ch[0],l,mid);
t[a].ch[1]=merge(t[a].ch[1],t[b].ch[1],mid+1,r);
t[a].size+=t[b].size;
return a;
}
int dfs()
{
int u,val;
scanf("%lld",&val);
if(!val)
{
int lc=dfs(),rc=dfs();
num1=num2=0;
u=merge(lc,rc,1,n);
ans+=min(num1,num2);//ans加上较小值
}
else u=update(1,n,val);
return u;
}
signed main()
{
scanf("%lld",&n);
dfs();
printf("%lld\n",ans);
return 0;
}