题意:
给定长度为n的数组a,和一个整数k
问有多少组(L,R),满足L<R且[1,L]与[R,n]拼起来形成序列逆序对数量不超过k
数据范围:n<=1e5,k<=1e18,a(i)<=1e9
解法:
如果(L,R)满足条件,那么(L,R+1)也满足条件
因此枚举L,找到满足条件的最小的R,那么答案就增加n-R+1
在枚举L的过程中,L右移,那么R要么不变要么右移,R的移动是单调的,
因为L右移逆序对会增加,如果逆序对总数超过k,那么R必须右移使得逆序对数量减少
那么定义一个指针R
L和R在移动过程中会导致逆序对变化,可以开两个树状数组分别存[1,L]和[R,n]中的数,
在L和R移动的时候维护[1,L]+[R,n]的逆序对变化。
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=1e5+5;
struct BIT{
int c[maxm];
int lowbit(int i){
return i&-i;
}
void add(int i,int t){
while(i<maxm)c[i]+=t,i+=lowbit(i);
}
int ask(int i){
int ans=0;
while(i)ans+=c[i],i-=lowbit(i);
return ans;
}
}T1,T2;
int a[maxm];
int b[maxm];
int n,k;
signed main(){
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>a[i];
}
//离散化
for(int i=1;i<=n;i++){
b[i]=a[i];
}
sort(b+1,b+1+n);
int num=unique(b+1,b+1+n)-b-1;
for(int i=1;i<=n;i++){
a[i]=lower_bound(b+1,b+1+num,a[i])-b;
}
//
int ans=0;
int cnt=0;//[1,l]+[r,n]的逆序对
int r=2;//初始状态l=1,r=2
//计算[2,n]的逆序对
for(int i=n;i>=2;i--){
T2.add(a[i],1);
cnt+=T2.ask(a[i]-1);//[r,n]内部的逆序对
}
//
for(int l=1;l<=n;l++){//枚举左端点l
T1.add(a[l],1);
cnt+=l-T1.ask(a[l]);
cnt+=T2.ask(a[l]-1);
while(l==r||(r<=n&&cnt>k)){//向右移动右端点r
T2.add(a[r],-1);
cnt-=T2.ask(a[r]-1);
cnt-=l-T1.ask(a[r]);
r++;
}
if(cnt>k||r>n)break;
ans+=n-r+1;
}
cout<<ans<<endl;
return 0;
}