思路
发现每个子树内的交换情况不会对子树外造成影响,所以可以利用贪心的思想,线段树合并找出当前子树的合并的最小值直接累加给答案即可
我脑补的线段树合并貌似不太优秀的样子。。。每次都要新建节点,学习了直接把y合并到x上的想法,但是因为#define int long long的坏习惯导致MLE,最后还是要写内存池。。。
代码
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#define int long long
using namespace std;
struct Node{
int lson,rson,sz;
}Seg[3000000];
int Nodecnt,ans,min1,min2,n;
queue<int> q;
int getnew(void){
if(q.size()){
int t=q.front();
q.pop();
Seg[t].lson=Seg[t].rson=Seg[t].sz=0;
return t;
}
return ++Nodecnt;
}
void throwin(int x){
q.push(x);
}
void build(int l,int r,int &o,int val){
// printf("%d %d %d %d\n",l,r,o,val);
if(!o)
o=getnew();
// printf("%d %d %d %d\n",l,r,o,val);
Seg[o].sz++;
if(l==r){
return;
}
int mid=(l+r)>>1;
if(val<=mid)
build(l,mid,Seg[o].lson,val);
else
build(mid+1,r,Seg[o].rson,val);
}
void merge(int &x,int y,int l,int r){
if(x*y==0){
x=x+y;
return;
}
min1+=Seg[Seg[x].rson].sz*Seg[Seg[y].lson].sz;
min2+=Seg[Seg[x].lson].sz*Seg[Seg[y].rson].sz;
if(l==r){
Seg[x].sz=Seg[x].sz+Seg[y].sz;
return;
}
Seg[x].sz=Seg[x].sz+Seg[y].sz;
int mid=(l+r)>>1;
merge(Seg[x].lson,Seg[y].lson,1,mid);
merge(Seg[x].rson,Seg[y].rson,mid+1,r);
throwin(y);
return;
}
int solve(void){
int x;
scanf("%lld",&x);
if(!x){
int lson,rson;
lson=solve();
rson=solve();
min1=0;
min2=0;
int t=lson;
merge(t,rson,1,n);
// printf("%d %d\n",min1,min2);
ans+=min(min1,min2);
return t;
}
else{
int t=0;
// printf("ok\n");
build(1,n,t,x);
// printf("ok\n");
return t;
}
return 0;
}
signed main(){
scanf("%lld",&n);
solve();
printf("%lld\n",ans);
return 0;
}