[codeforces 1269E] K Integers 逆序对+平移+树状数组+二分
总目录详见https://blog.csdn.net/mrcrack/article/details/103564004
在线测评地址https://codeforces.com/contest/1269/problem/E
Problem | Lang | Verdict | Time | Memory |
---|---|---|---|---|
E - K Integers | GNU C++11 | Happy New Year! | 171 ms | 3700 KB |
//直觉是求逆序对,试了样例,发现确实如此,不过,怎么移,还是有些问题。
5 4 3 2 1
逆序对个数 0 1 2 3 4
求和 0 1 3 6 10
//求逆序对,有枚举/归并排序/树状数组/线段树 等办法,思考后,决定用 树状数组
//提交,未通过。
再举一例
Input:
5
2 4 1 5 3
Ouput:
0 2 3 4 4
2 4 1 5 3
f(1)=0
2 4 1 5 3
4 2 1 5 3
4 1 2 5 3
f(2)=2
2 4 1 5 3
4 2 1 5 3
4 2 1 3 5
4 1 2 3 5
f(3)=3
2 4 1 5 3
2 4 1 3 5
2 1 4 3 5
2 1 3 4 5
1 2 3 4 5
f(4)=4
2 4 1 5 3
2 1 4 5 3
1 2 4 5 3
1 2 4 3 5
1 2 3 4 5
f(5)=4
//此文https://blog.csdn.net/Q755100802/article/details/103664555思路不错,摘抄如下
//提交AC.2019-12-29 16:10
#include <stdio.h>
#define LL long long
#define maxn 200010
int pos[maxn],n;
LL sum1[maxn],sum2[maxn];//sum1数的个数,sum2数的位置之和
int lowbit(int x){
return x&-x;
}
void add(LL *sum,int x,int delta){
for(int i=x;i<=n;i+=lowbit(i))sum[i]+=delta;
}
LL getSum(LL *sum,int x){
LL ret=0;
for(int i=x;i>=1;i-=lowbit(i))ret+=sum[i];
return ret;
}
int Bisection(int i){
int l=1,r=n+1,mid;
while(l+1<r){
mid=(l+r)/2;
if(getSum(sum1,mid)*2<=i)l=mid;
else r=mid;
}
return l;
}
int main(){
int a,i,mid,cnt;
LL ans1=0,ans2,sum;
scanf("%d",&n);
for(i=1;i<=n;i++)scanf("%d",&a),pos[a]=i;//pos[a]=i数值a所处的位置i即pos[a]
for(i=1;i<=n;i++){//将数字i自小到大进行处理
ans2=0;
add(sum1,pos[i],1);
add(sum2,pos[i],pos[i]);
ans1+=i-getSum(sum1,pos[i]);//getSum(sum1,pos[i])在pos[i]之前小于等于i的数的数量,逆序对
mid=Bisection(i);
cnt=getSum(sum1,mid),sum=getSum(sum2,mid);
ans2+=(LL)mid*cnt-sum-(LL)(0+cnt-1)*cnt/2;//mid左侧
cnt=i-cnt,sum=getSum(sum2,n)-sum;//此处极为关键
ans2+=sum-(LL)(mid+1)*cnt-(LL)(0+cnt-1)*cnt/2;//mid右侧
if(i>=2)printf(" ");
printf("%lld",ans1+ans2);
if(i==n)printf("\n");
}
return 0;
}