hdu 5196 DZY Loves Inversions && BestCoder Round #35

问题描述
DZY有一个序列
  
  
   
   a
  
  ,一共由
  
  
   
   n
  
  个正整数组成,下标为
  
  
   
   1
  
  
  
  
   
   n
  
  。我们定义第
  
  
   
   i
  
  个数为
  
  
   
   ai
  
  。
DZY每次给定一个数对(
  
  
   
   l,r
  
  )
  
  
   
   (lr)
  
  ,他想计算有多少个数对(
  
  
   
   i,j
  
  ),满足
  
  
   
   lijr
  
  ,且序列 
  
  
   
   b=aiai+1aj
  
   有恰好
  
  
   
   k
  
  个逆序对。而且,DZY会询问你
  
  
   
   q
  
  次噢。

题解:

1.考虑如何计算一个区间中有多少个子区间的逆序对数小于等于
  
  
   
   K
  
  。这样做两遍就能算出恰好等于
  
  
   
   K
  
  的了。
2.对于
   
   
    
    i(1in)
   
   ,fi表示[i,fi]逆序对数小于等于
  
  
   
   K
  
  ,且fi的值最大。显然fi单调不降,我们可
以通过用两个指针扫一遍,利用树状数组计算出f数组。
3.因为ai很大,但是只有1e5个,所以可以进行压缩映射成至多1 -- 1e5,才可以用树状数组维护逆序对数
4.询问l,r时,可以二分找到最大的f[i] <= r,维护一个前缀和
s = (f[l] - l + 1) + .... + (f[l + i] - l - i + 1)表示这些区间所能组成的最大子区间数
5.对于[i + 1,r],f[i + 1]肯定大于r,所以对于区间[i + 1,r],[i + 2,r]....[r,r],维护另一个前缀和
s1 = (r - i - 1 + 1) + ....... + (r - r + 1)表示这些区间所能组成的最大子区间数
总结:
1.看了bestcoder的前两点提醒才想到的,感觉又是一个运用前缀和和单调性思想的题目
2.debug代码的时候还算满意,没有死扣瞎提交,以后坚持,首先争取不犯设计上的错误,然后就要平静的查错

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
#define MAXN 100005
#define lowbit(i) (i & -i)
int num[MAXN],bit[MAXN],n,q;
LL fk1[MAXN],fk2[MAXN],s1[MAXN],s2[MAXN],k;
struct Node
{
    int v,id;
    bool operator < (const Node & c)const{
        return v < c.v;
    }
}node[MAXN];
int sum(int i)
{
    int total = 0;
    for(;i;i -= lowbit(i))
        total += bit[i];
    return total;
}
void modify(int i,int key)
{
    for(;i <= n;i += lowbit(i))
        bit[i] += key;
}
void findf(LL k,LL * s,LL * ss)
{
    LL cur = 0;
    memset(bit,0,sizeof(bit));
    for(int i = 1,j = 0;i <= n;i++)
    {
        while(j <= n && cur <= k)
        {
            if(++j > n)break;
            cur += (j - i) - sum(num[j]);
            modify(num[j],1);
        }
        cur -= sum(num[i] - 1);
        modify(num[i],-1);
        s[i] = j - 1;
        ss[i] = ss[i - 1] + j - i;
    }
}
LL query(int l,int r)
{
    int ind = upper_bound(fk1 + l,fk1 + r + 1,r) - fk1 - 1;
    LL cur = r - ind;
    LL ans1 = (cur + 1) * cur / 2 + s1[ind] - s1[l - 1];
    if(!k)return ans1;
    ind = upper_bound(fk2 + l,fk2 + r + 1,r) - fk2 - 1;
    cur = r - ind;
    LL ans2 = (cur + 1) * cur / 2 + s2[ind] - s2[l - 1];
    return ans1 - ans2;
}
void solve()
{
    findf(k,fk1,s1);
    if(k)findf(k - 1,fk2,s2);
    int l,r;
    for(int i = 0;i < q;i++)
    {
        scanf("%d%d",&l,&r);
        LL ans = query(l,r);
        printf("%I64d\n",ans);
    }
}
int main()
{
    while(~scanf("%d%d%I64d",&n,&q,&k))
    {
        for(int i = 1;i <= n;i++)
        {
            scanf("%d",&num[i]);
            node[i].v = num[i];
            node[i].id = i;
        }
        sort(node + 1,node + n + 1);
        num[node[1].id] = 1;
        for(int i = 2;i <= n;i++)
            if(node[i].v == node[i - 1].v)
                num[node[i].id] = num[node[i - 1].id];
            else
                num[node[i].id] = num[node[i - 1].id] + 1;
        solve();
    }
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值