What Goes Up Must Come Down(思维+逆序对构造LIS山峰)

    科技2022-08-31  102

    https://ac.nowcoder.com/acm/contest/7831/F


    考虑题意:对于每一个数来说,它最终要不左边的数全比它小,要不右边的数全比他小。

    交换相邻的两个数,其他数字的相对位置不变。

    那么转换一下思维,最终换的次数就是某个数被其他数交换的次数,然后累加每个数。

    某个数要和其他数交换的次数,就是逆序对的个数。

    比如3 3 3  0 1 3.这个1要换的次数要不是右边的1个3,要不是左边的3个3.中间过来的次数是交给0 的。

    每次移动相邻两个位置,所以你只需要判断它左边比它大的数的个数和右边比它大的个数的最小值。

    然后选小的那个交换。

    求逆序对用树状数组即可。注意作桶的时候最大值开大。

    #include<iostream> #include<vector> #include<queue> #include<cstring> #include<cmath> #include<map> #include<set> #include<cstdio> #include<algorithm> #define debug(a) cout<<#a<<"="<<a<<endl; #define lowbit(x) x&(-x); using namespace std; const int maxn=1e5+100; typedef long long LL; const LL inf=1e5+5; LL a[maxn],tree1[maxn],c[maxn],n; void add(LL x,LL d) { while(x<=inf){///桶要开大 tree1[x]+=d; x+=lowbit(x); } } LL getsum(LL x) { LL sum=0; while(x>0) { sum+=tree1[x]; x-=lowbit(x); } return sum; } int main(void) { cin.tie(0);std::ios::sync_with_stdio(false); cin>>n; for(LL i=1;i<=n;i++) cin>>a[i]; LL ans=0; memset(tree1,0,sizeof(tree1)); for(LL i=1;i<=n;i++) { add(a[i],1); c[i]=i-getsum(a[i]); } memset(tree1,0,sizeof(tree1)); for(LL i=n;i>=1;i--) { add(a[i],1); c[i]=min(c[i],n-i+1-getsum(a[i])); } for(LL i=1;i<=n;i++) ans+=c[i]; cout<<ans<<endl; return 0; }

     

    Processed: 0.009, SQL: 9