( ext{Solution:})
这东西的输入有些新奇,写一个递归函数即可。
观察到题目要求的是子树交换,没有修改。而且,显然地,这样的结构必然要满足对于它每一个子结构都达到最优。而且任意一棵子树内部交换对答案的影响是独立的。
考虑从下往上维护答案,每次需要合并两棵子树的信息并选择小的逆序对。
合并信息,自然想到了线段树合并。
考虑如何在线段树合并的时候计算出逆序对:用类似于 cdq分治 的思想,考虑计算跨越区间的答案:用位置靠前的节点数乘位置靠后的节点数,并同时满足前面的数小于后面的。然后再分别递归处理子树(左右区间)。
这样就可以做到一只 $log $ 了。
#include<bits/stdc++.h>
using namespace std;
const int MAXN=5e6+10;
int n,root,rt[MAXN],cnt;
int ls[MAXN],rs[MAXN],sum[MAXN];
int node,pa[MAXN],tot,head[MAXN];
long long K1,K2,ans[MAXN];
inline int read(){
int s=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch)){
s=s*10-48+ch;
ch=getchar();
}
return s;
}
inline long long Min(long long x,long long y){return x<y?x:y;}
struct E{int nxt,to;}e[MAXN];
inline void add(int x,int y){e[++tot]=(E){head[x],y};head[x]=tot;}
inline void pushup(int x){sum[x]=sum[ls[x]]+sum[rs[x]];}
void change(int &x,int L,int R,int pos,int v){
if(!x)x=++node;
if(L==R){
sum[x]+=v;
return;
}
int mid=(L+R)>>1;
if(pos<=mid)change(ls[x],L,mid,pos,v);
else change(rs[x],mid+1,R,pos,v);
pushup(x);
}
int merge(int x,int y,int l,int r,int pos){
if(!x||!y)return x+y;
if(l==r){sum[x]+=sum[y];return x;}
int mid=(l+r)>>1;
K1+=1ll*sum[rs[x]]*sum[ls[y]];
K2+=1ll*sum[ls[x]]*sum[rs[y]];
ls[x]=merge(ls[x],ls[y],l,mid,pos);
rs[x]=merge(rs[x],rs[y],mid+1,r,pos);
pushup(x);return x;
}
void Read(int &x){
if(!x)x=++cnt;
int v=read();
if(v==0){
int lson=0;
Read(lson);
int rson=0;
Read(rson);
pa[lson]=pa[rson]=x;
add(lson,x);add(rson,x);
add(x,lson);add(x,rson);
}
else {
change(rt[x],1,n,v,1);
ans[x]=0;
return;
}
}
void dfs(int x){
for(int i=head[x];i;i=e[i].nxt){
int j=e[i].to;
if(j==pa[x])continue;
dfs(j);
K1=K2=0;
rt[x]=merge(rt[x],rt[j],1,n,x);
ans[x]=Min(K1,K2);
}
}
int main(){
n=read();
Read(root);
dfs(root);
long long res=0;
for(int i=1;i<=cnt;++i)res+=ans[i];
printf("%lld
",res);
return 0;
}