Codeforces Round #136 (Div. 1)E(尺取法+树状数组)

题目链接:https://codeforces.com/contest/220/problem/E

题意:只保留a数列中1..l和r..n的数构成b数列,然后b数列的逆序对数小于等于k.问这样的l,r的对数。

分析:树状数组+尺取

枚举l,每次找到最小的满足题意的r,对答案的贡献是n-r+1,然后用两个树状数组,分别维护增加或者减少一个数的时候,前半段和后半段对逆序数的影响。

Ac code:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
typedef long long ll;
ll c[2][maxn];
int a[maxn],n;
vector<int>p;
int lowbit(int x)
{
    return x&(-x);
}
void update(bool flag,int x,ll val)
{
    if(!flag) x=n-x+1;
    while(x<=n){
        c[flag][x]+=val;
        x+=lowbit(x);
    }
}
ll query(bool flag,int x)
{
    if(!flag) x=n-x+1;
    ll ans=0;
    while(x>0){
        ans+=c[flag][x];
        x-=lowbit(x);
    }
    return ans;
}
int Hash(int x)
{
    return lower_bound(p.begin(),p.end(),x)-p.begin()+1;
}
int main()
{
    ll k;
    cin>>n>>k;
    for(int i=1;i<=n;i++) cin>>a[i],p.push_back(a[i]);
    sort(p.begin(),p.end());
    p.erase(unique(p.begin(),p.end()),p.end());
    ll ans=0,cnt=0;
    for(int i=n;i>=1;--i)///起初l==r,b数组就是a数组,逆序对数就是a数组的逆序对数
    {
        a[i]=Hash(a[i]);
        cnt+=query(1,a[i]-1);///倒着求<a[i]的数那么刚好就是逆序对
        update(1,a[i],1);
    }
    int r=1;
    for(int l=1;l<=n;l++)
    {
        cnt+=query(0,a[l]+1)+query(1,a[l]-1);///query(0,x)表示查询大于>=x的数的个数,query(1,x)表示查询<=x的数的个数
        update(0,a[l],1);///左端点增加,插入a[l]
        while((r<=l||cnt>k)&&r<=n){
            cnt-=query(0,a[r]+1)+query(1,a[r]-1);///不满足条件时右端点右移,找到最小的满足条件的r
            update(1,a[r],-1);
            ++r;
        }
        ans+=n-r+1;///如果r继续右移逆序对数会减少,所以后面肯定满足条件,故答案可以直接加上n-r+1
    }
    cout<<ans<<endl;
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值