$n \leq 300000$的一个排列,每次能交换相邻两个数,并且有一次机会交换不相邻的两个数,可以不用这个机会。问使这个排列升序最少操作几次。
如果没有“不相邻”,那就是当年入门的时候学的逆序对了。也就是说,这次机会希望把逆序对数尽可能减少。把排列变成点放在二维平面上,$(i,a_i)$,可以发现交换$(i,a_i)$和$(j,a_j)$减少的答案,就是他们为端点的矩形里的点数(不含他们两个)的两倍,且必须$i>j,a_i<a_j$,否则就是答案变大这么多了。
进一步观察,如果有$k<j,a_k>a_j$,那么所有的交换$(j,i),j<i$都是不如交换$(k,i)$优的(前者的对应矩形被后者完全包含),因此左上端点一定在从$(1,a_1)$开始的一个递增序列(记为$L$)里。右下端点同理,一定在从$(n,a_n)$往前走的一个越走越小的序列(记为$R$)里,可以处理出来,但点的数量级是没变的。
法一:$R$中的点和$L$中的点配满足决策单调性。证明:如下图,$x,y$是$R$中的点,$p,q$是$L$中的点,如果在$x$处最优决策点是$q$,$S$表示一个区域里有几个点$(i,a_i)$,那么有$S_1>S_2+S_3$,那么在$y$处决策的时候,就有$S_1+S_4>S_2$,因此$p$还是不如$q$优。
于是整体二分,用主席树计算矩形点数,俩log。
1 //#include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 //#include<time.h> 5 //#include<math.h> 6 //#include<set> 7 #include<queue> 8 //#include<bitset> 9 //#include<vector> 10 #include<algorithm> 11 #include<stdlib.h> 12 using namespace std; 13 14 #define LL long long 15 LL qread() 16 { 17 char c; LL s=0; int f=1; while ((c=getchar())<'0' || c>'9') (c=='-') && (f=-1); 18 do s=s*10+c-'0'; while ((c=getchar())>='0' && c<='9'); return s*f; 19 } 20 21 //Pay attention to '-' , LL and double of qread!!!! 22 23 int n; 24 #define maxn 300011 25 int a[maxn]; 26 struct BIT 27 { 28 int a[maxn],n; 29 void clear(int N) {n=N;} 30 void add(int x,int v) {for (;x<=n;x+=x&-x) a[x]+=v;} 31 int query(int x) {int ans=0; for (;x;x-=x&-x) ans+=a[x]; return ans;} 32 }t; 33 34 int root[maxn]; 35 struct SMT 36 { 37 struct Node{int ls,rs,sum;}a[maxn*20]; 38 int size,n; 39 void clear(int N) {n=N; size=0;} 40 void in(int &x,int y,int L,int R,int v) 41 { 42 x=++size; a[x].sum=a[y].sum+1; 43 if (L==R) return; 44 int mid=(L+R)>>1; 45 if (v<=mid) in(a[x].ls,a[y].ls,L,mid,v),a[x].rs=a[y].rs; 46 else in(a[x].rs,a[y].rs,mid+1,R,v),a[x].ls=a[y].ls; 47 } 48 void in(int &x,int y,int v) {in(x,y,1,n,v);} 49 int Query(int x,int L,int R,int ql,int qr) 50 { 51 if (ql<=L && R<=qr) return a[x].sum; 52 int mid=(L+R)>>1,ans=0; 53 if (ql<=mid) ans+=Query(a[x].ls,L,mid,ql,qr); 54 if (qr>mid) ans+=Query(a[x].rs,mid+1,R,ql,qr); 55 return ans; 56 } 57 int query(int x,int ql,int qr) {return Query(x,1,n,ql,qr);} 58 }tt; 59 60 int sl[maxn],tl,sr[maxn],tr; 61 int query(int x,int y) 62 { 63 x=sl[x]; y=sr[y]; 64 if (y<x || a[x]<a[y]) return 0; 65 int rx=root[x],ry=root[y-1]; 66 return tt.query(ry,a[y]+1,a[x]-1)-tt.query(rx,a[y]+1,a[x]-1); 67 } 68 69 int f[maxn]; 70 void solve(int pl,int pr,int L,int R) 71 { 72 if (L>R) return; 73 int mid=(L+R)>>1,id=0; 74 for (int i=pr,tmp;i>=pl;i--) if ((tmp=query(i,mid))>f[mid]) 75 { 76 f[mid]=tmp; 77 id=i; 78 } 79 solve(pl,id,L,mid-1); solve(id,pr,mid+1,R); 80 } 81 82 int main() 83 { 84 n=qread(); 85 for (int i=1;i<=n;i++) a[i]=qread(); 86 LL ans=0; t.clear(n); 87 for (int i=n;i;i--) 88 { 89 ans+=t.query(a[i]-1); 90 t.add(a[i],1); 91 } 92 93 tt.clear(n); 94 for (int i=1;i<=n;i++) tt.in(root[i],root[i-1],a[i]); 95 96 tl=tr=0; 97 for (int i=1;i<=n;i++) if (!tl || a[sl[tl]]<a[i]) sl[++tl]=i; 98 for (int i=n;i;i--) if (!tr || a[sr[tr]]>a[i]) sr[++tr]=i; 99 for (int i=1,to=tr>>1;i<=to;i++) sr[i]^=sr[tr-i+1]^=sr[i]^=sr[tr-i+1]; 100 101 solve(1,tl,1,tr); 102 int tmp=0; 103 for (int i=1;i<=tr;i++) tmp=max(tmp,f[i]); 104 ans-=tmp*2; 105 printf("%lld\n",ans); 106 return 0; 107 }