真难写,,
考虑冒泡排序,每一次交换相邻的减少 1 对逆序对,那么我们肯定要第二种操作尽量多地减少逆序对。
考虑第二种操作 swap(i,j) 减少的逆序对数量:\(\forall k\in(i,j),h_j<h_k<h_i\),\(k\) 的个数乘二。
考虑抽象到二维平面,点坐标为 \((i,h_i)\),那么就是以 \((i,h_i)\) 为左上角,\((j,h_j)\) 为右下角的矩形内部点数量。
但这样暴力算还是 \(O(n^2\log n)\)。
看看选择的点有什么性质。
自己画画图,既然 \(i\) 为左上角,那么 \(i\) 应该是单增的,所以应该选择的是前缀最大值的点。
这时候可能有小可爱跟我一样 SB,想了好久 \(j\) 是前缀最小值还是后缀最小值。
即
黑色的是前缀最大值的点,红色是前缀最小值,蓝色是后缀最小值。
然后我思考了很久发现不知道选择是前缀还是后缀。
事实上图画错了!
所以显然是右端点是后缀最小值的点。
这样暴力维护也没用,找找性质,枚举右端点?可惜左端点并不单增。
考虑正难则反,让 \(k\) 去贡献每对 \((i,j)\),即每个矩形。
需要满足的是 \(i<k,h_i>h_k,j>k,h_j<h_k\),发现因为连续区间且维护的前缀最大、后缀最小都单调,因此二分找出合法区间,变成二维平面矩形加、点最大值。扫描线即可。
//swap(i,j)-> -= i<k<j ,a[i]>a[k],a[j]<a[k],a[j]<a[k]<a[i]
//二维平面(i,a[i]),矩形查
#include <bits/stdc++.h>
#define int long long
#define pb push_back
using namespace std;
#define N (int)(3e5+5)
int n,a[N],sum[N];
int lowbit(int x) {
return x&(-x);
}
void add(int x,int v) {
while(x<=n) sum[x]+=v,x+=lowbit(x);
}
int qry(int x) {
int res=0; while(x) res+=sum[x],x-=lowbit(x); return res;
}
namespace zzx {
int Ls[N*32],Rs[N*32],sum[N*32],rt[N],tot=0;
void update(int pre,int &cur,int l,int r,int pos) {
if(!cur) cur=++tot;
sum[cur]=sum[pre]+1;
if(l==r) return ;
int mid=(l+r)>>1;
if(pos<=mid) Rs[cur]=Rs[pre],update(Ls[pre],Ls[cur],l,mid,pos);
else Ls[cur]=Ls[pre],update(Rs[pre],Rs[cur],mid+1,r,pos);
}
int query(int pre,int cur,int l,int r,int cl,int cr) {
if(cl>cr) return 0;
int qwq=sum[cur]-sum[pre];
if(cl<=l&&r<=cr) return qwq;
int mid=(l+r)>>1,res=0;
if(cl<=mid) res=query(Ls[pre],Ls[cur],l,mid,cl,cr);
if(cr>mid) res+=query(Rs[pre],Rs[cur],mid+1,r,cl,cr);
return res;
}
}
using namespace zzx;
vector<int>L,R;
struct node {
int l,r,v;
node() {
}
node(int nl,int nr,int nv) {
l=nl; r=nr; v=nv;
}
};
vector<node>p[N];
namespace XDS {
#define ls (cur<<1)
#define rs (ls|1)
int mx[N<<2],tag[N<<2];
void push_up(int cur) {
mx[cur]=max(mx[ls],mx[rs]);
}
void push_down(int cur) {
if(tag[cur]==0) return ;
mx[ls]+=tag[cur]; mx[rs]+=tag[cur];
tag[ls]+=tag[cur]; tag[rs]+=tag[cur];
tag[cur]=0;
}
void update(int cur,int l,int r,int cl,int cr,int x) {
if(cl<=l&&r<=cr) {
mx[cur]+=x; tag[cur]+=x; return ;
}
push_down(cur);
int mid=(l+r)>>1;
if(cl<=mid) update(ls,l,mid,cl,cr,x);
if(cr>mid) update(rs,mid+1,r,cl,cr,x);
push_up(cur);
}
int query(int cur,int l,int r,int cl,int cr) {
if(cl<=l&&r<=cr) {
// cout<<mx[cur]<<'\n';
return mx[cur];
}
push_down(cur);
int mid=(l+r)>>1,res=0;
if(cl<=mid) res=query(ls,l,mid,cl,cr);
if(cr>mid) res=max(res,query(rs,mid+1,r,cl,cr));
return res;
}
}
using namespace XDS;
bool vis[N];
signed main() {
//freopen("columns9_3.in","r",stdin);
cin.tie(0);
std::ios::sync_with_stdio(false);
cin>>n; int ans=0;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) {
ans+=qry(n)-qry(a[i]);
add(a[i],1);
}
for(int i=1;i<=n;i++) update(rt[i-1],rt[i],1,n,a[i]);
// cout<<ans<<endl;
int mx=0; a[0]=0;
for(int i=1;i<=n;i++) {
//cout<<a[i]<<" "<<a[mx]<<endl;
if(a[i]>a[mx]) {
mx=i; L.pb(i); vis[i]=1;//cout<<"?";
}
}
a[0]=(int)(2e18); int mi=0;
for(int i=n;i>=1;i--) {
if(a[i]<a[mi]) {
mi=i; R.pb(i); vis[i]=1;
}
}
reverse(R.begin(),R.end());
//cout<<L.size()<<" "<<R.size()<<'\n';
for(int x=1;x<=n;x++) {
if(vis[x]) continue;
int l=0,r=L.size()-1,res=-1,xl=-1,xr=-1,yl=-1,yr=-1;
while(l<=r) {
int mid=(l+r)>>1;
if(a[L[mid]]>a[x]) res=mid,r=mid-1;
else l=mid+1;
}
// if(res!=-1) cout<<res<<endl;
if(res!=-1) {
xl=L[res]; xr=x-1;
}
l=0; r=R.size()-1; res=-1;
while(l<=r) {
int mid=(l+r)>>1;
if(a[R[mid]]<a[x]) res=mid,l=mid+1;
else r=mid-1;
}
if(res!=-1) {
yl=x+1; yr=R[res];
}
if(xl!=-1&&yl!=-1) {
if(xl>xr||yl>yr) continue;
// cout<<xl<<" "<<xr<<" "<<yl<<" "<<yr<<'\n';
p[xl].pb(node(yl,yr,1)); p[xr+1].pb(node(yl,yr,-1));
}
}
int res=0;
for(int i=1;i<=n;i++) {
for(node x:p[i]) {
// cout<<x.l<<" "<<x.r<<endl;
update(1,1,n,x.l,x.r,x.v);
}
res=max(res,query(1,1,n,1,n));
}
cout<<ans-2ll*res<<'\n';
return 0;
}