分析:这题据说老掉牙了,但是对我来说还是挺新的,毕竟刷题少。题目可以拆成两部分。
第一部分,把1 ~ K 的数聚集在一起。
将原数组中1 ~ K 的数中位置在最中间的数称作中间数位置为pos,肯定是把其他 K - 1 个数向中间数靠拢最划算。令原来这 K 个数的位置为 ,移动之后位置为 ,那么在这一部分对答案的贡献 拆开算 对于 t 的这一部分可以直接是连续的一段数,等差数列算一下。对于 p 的部分
用树状数组计算。对 pos ,需要二分得到
第二部分,经典的逆序对数数,cnt 的计算挺有意思的,之前想的很麻烦,这样累加不管是思维还是代码都方便很多。
code :
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 2e5 + 5; ll b[maxn],c[maxn],p[maxn],n; ll lowbit(ll x){return x&-x;} void update(ll *num,ll x,ll y) { while(x<=n) { num[x]+=y; x+=lowbit(x); } } ll query(ll *num,ll x) { ll sum=0; while(x) { sum+=num[x]; x-=lowbit(x); } return sum; } int main() { ll x; scanf("%lld",&n); for(ll i=1;i<=n;i++) { scanf("%lld",&x); p[x]=i; } ll cnt=0; // 当前逆序对,是个累积的变量 for(ll i=1;i<=n;i++) { ll sum=0; update(b,p[i],1); update(c,p[i],p[i]); cnt+=i-query(b,p[i]); if(i==1) { printf("0 "); continue; } ll l=1,r=n,pos; while(l<=r) { ll mid=(l+r)>>1; if(query(b,mid)*2<=i) { pos=mid,l=mid+1; } else r=mid-1; } ll cntL,cntR,idsumL,idsumR; cntL=query(b,pos);//包含pos位置 cntR=i-cntL; idsumL=query(c,pos); idsumR=query(c,n)-idsumL; sum+=(pos+pos-cntL+1)*cntL/2-(pos+1+pos+cntR)*cntR/2; sum+=idsumR-idsumL; printf("%lld ",sum+cnt); } return 0; }