G What Goes Up Must Come Down
题意:
输入第一行
n
(
1
e
5
)
n(1e5)
n(1e5),表示有
n
n
n个数。
输入第二行
a
1
,
a
2
,
…
,
a
n
(
1
e
5
)
a_1,a_2,\dots,a_n(1e5)
a1,a2,…,an(1e5)。
一次操作为交换相邻的两个数,问经过最少几次操作可以把序列变成前面不递减后面不递增。
题解:
对于数交换,肯定先把比较小的数先交换到两端。
现在分析对于一个数
a
i
a_i
ai,把它移动到左端,它的贡献为左边比它大的数有多少个,相同的移动到右端,它的贡献为右边比它大的数有多少个,取贡献小的就好了;对于每一个数都这样子取,把贡献加到答案上就结束了。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+9;
#define ll long long
int n,a[N],l[N],r[N];
int f[N];
void add(int x,int y){for(;x<N;x+=(x&-x))f[x]+=y;}
int query(int x){int ans=0;while(x)ans+=f[x],x-=(x&-x);return ans;}
int main(){
//freopen("tt.in","r",stdin),freopen("tt.out","w",stdout);
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)add(a[i],1),l[i]=query(N-1)-query(a[i]);
memset(f,0,sizeof(f));
for(int i=n;i>=1;i--)add(a[i],1),r[i]=query(N-1)-query(a[i]);
memset(f,0,sizeof(f));
ll ans=0;
for(int i=1;i<=n;i++)ans+=min(l[i],r[i]);
cout<<ans<<endl;
return 0;
}