Description
逆序对是一个非常经典的问题,但是逆序对虽然经典,但却是一个非常困难的问题,因为一个序列的逆序对数可能太多了。所以为了简化问题,我们给定一个长度为N的序列z和一个参数k,我们希望知道有多少个(L,R)满足1 <= L < R <= N,且z1, z2, ⋯ , zL, zR, ⋯ , zN的逆序对个数不超过k。
Input
第一行两个整数N, k。
接下来一行N个整数代表序列。
Output
一行一个整数代表答案。
Sample Input 1
3 1
1 3 2
Sample Output 1
3
Sample Input 2
5 2
1 3 2 1 7
Sample Output 2
6
Data Constraint
对于100%的数据,1 <= N <= 10^5, 1 <= zi <= 10^9, k <= 10^18,有部分分。
解题思路
先离散化zi的值,用一个树状数组记录一个数前面比它大的数的个数,另一个树状数组记录一个数后面比它小的数的个数。从1至n-1枚举每一个L,R从2开始,对于每一个L,R向右找到当时逆序对数不超过k的最小值,此时的R~n都可以是可行的R的方案,加入到答案里。显然R是递增的。
代码
#include<bits/stdc++.h>
#define lowbit(x) (x&(-x))
#define N 100001
#define LL long long
using namespace std;
int n,b[N+5],a[N+5],tree[2][N+5];
LL k,now=0,ans=0;
inline void add(int x,int num,int opt){
while(x<=N){
tree[opt][x]+=num;
x+=lowbit(x);
}
}
inline int search(int x,int opt){
int re=0;
while(x){
re+=tree[opt][x];
x-=lowbit(x);
}
return re;
}
int main(){
scanf("%d%lld",&n,&k);
for(int i=1;i<=n;++i) scanf("%d",&a[i]),b[i]=a[i];
sort(b+1,b+n+1);unique(b+1,b+n+1);
for(int i=1;i<=n;++i) a[i]=lower_bound(b+1,b+n+1,a[i])-b;
for(int i=n;i>=2;--i){
now+=search(a[i]-1,1);
add(a[i],1,1);
}
LL R=2;
for(int i=1;i<=n-1;++i){
now+=search(N-a[i]-1,0)+search(a[i]-1,1);
add(N-a[i],1,0);
while((now>k || R<=i) && R<=n){
now-=search(N-a[R]-1,0)+search(a[R]-1,1);
add(a[R],-1,1);
++R;
}
if(R==n+1) break;
ans+=n-R+1;
}
printf("%lld",ans);
return 0;
}
本文介绍了一种解决逆序对计数问题的有效算法。通过离散化输入序列,并使用两个树状数组来分别记录每个元素前后的逆序对数量,从而在O(n log n)的时间复杂度内找到所有子序列中逆序对数量不超过k的组合。
888

被折叠的 条评论
为什么被折叠?



