Codeforces220 E. Little Elephant and Inversions(树状数组)

题意:

给定长度为n的数组a,和一个整数k
问有多少组(L,R),满足L

数据范围: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
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;
}

你可能感兴趣的:(Codeforces220 E. Little Elephant and Inversions(树状数组))