C. K Integers(逆序对+二分中间位置)

https://codeforces.com/contest/1268/problem/C


题意:

思路:

挺不错的一道题

参考:

https://blog.csdn.net/Q755100802/article/details/103664555

相当不错

#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define lowbit(x) x&(-x)
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=2e5+1000;
typedef long long LL;
LL tree1[maxn],tree2[maxn];
///tree1[]:记录逆序数的数量;tree2[]:记录位置的总和
inline LL read(){LL x=0,f=1;char ch=getchar();	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;}
LL pos[maxn];
void add(LL tree[],LL x,LL d){
     while(x<maxn){
        tree[x]+=d;
        x+=lowbit(x);
     }
}
LL getsum(LL tree[],LL x){
     LL sum=0;
     while(x>0){
        sum+=tree[x];
        x-=lowbit(x);
     }return sum;
}
int main(void){
   cin.tie(0);std::ios::sync_with_stdio(false);
   LL n;cin>>n;
   for(LL i=1;i<=n;i++){
       LL x;cin>>x;
       pos[x]=i;///记录每个数的位置
   }
   LL nxd=0;///逆序对的和
   vector<LL>v;
   for(LL i=1;i<=n;i++){///枚举每个数
       add(tree1,pos[i],1);
       add(tree2,pos[i],pos[i]);
       nxd+=i-getsum(tree1,pos[i]);
       LL l=1;LL r=n;
       while(l<r){
          LL mid=(l+r+1)>>1;
          if( getsum(tree1,mid)*2<=i){///尽可能放到中间
             l=mid;
          }
          else r=mid-1;
       }
      LL res=0;
      LL cnt=getsum(tree1,l);///前面的数字数量
      LL sum=getsum(tree2,l);///pos之和
      ///将mid左边的数靠拢到mid附近的花费
      res+=l*cnt-sum-cnt*(cnt-1)/2;
      ///将mid右边的数靠拢到mid附近的花费
      cnt=i-cnt;sum=getsum(tree2,n)-sum;
      res+=sum-(l+1)*cnt-cnt*(cnt-1)/2;

      v.push_back(res+nxd);
   }
   for(auto i:v){
      cout<<i<<" ";
   }cout<<"\n";
   return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值