区间平均值(逆序对)

优化 同时被 3 个专栏收录
100 篇文章 0 订阅
103 篇文章 0 订阅
3 篇文章 0 订阅

【问题描述】

有 个数,随机选择一段区间,如果这段区间的所有数的平均值在[l , r]中则 你比较厉害。求你比较厉害的概率。

【输入格式】

第一行有三个数 N,l ,r,含义如上描述。 接下一行有 个数代表每一个数的值。

【输出格式】
输出一行一个分数 代表答案,其中 , 互质。如果答案为整数则直接输出该整数即可。

【样例输入 1】

4 2 3
3 1 2 4

【样例输出 1】

7/10

【样例输入 2】

4 1 4
3 1 2 4

【样例输出 2】

1

【样例解释】

塔外面有棵树。

【数据规模与约定】

对于30%的数据,1 ≤N≤ 10^4。 对于60%的数据,1 ≤N≤ 10^5。
对于100%的数据,1 ≤N≤ 5 × 10^5, 0 < l≤r≤ 100。

要求 区间平均值 >=l&& < =r 的个数

现在我们来求区间平均值在1~r的个数和1~l(不包括l)的个数 前减后即为所求
以求1~r为例
(a[i]+a[i+1]+……+a[i+k-1])/k<=r
(a[i]+a[i+1]+……+a[i+k-1])<=k*r
(a[i]+a[i+1]+……+a[i+k-1])-k*r<=0
(a[i]-r)+(a[i+1]-r)+……+(a[i+k-1]-r)<=0
令c[i]=a[i]-r得到一个新数组c
即求c数组区间和<=0的个数
令s为数组c的前缀和数组
c[i]+c[i+1]+…+c[i+k-1]<=0
s[i+k-1]-s[i]<=0
s[i+k-1]<=s[i]

i< i+k-1
s[i]>=s[i+k-1]
即求s数组逆序对数

同理,1~l的也一样,但是< l

那么求逆序对,数值可能为负,所以我们需要离散化一下。

我们将s数组拷贝一下到b数组。
然后我们对s数组排序并用unique函数去重。
然后在s数组中找b[i]的位置并赋值给b[i],那么到最后我们对b数组求逆序对即是原s数组的逆序对。
这里将数值转化为没有空闲的位置坐标,降低树状数组的空间复杂度,而且也解决了负数的问题。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define LL long long
#define M 500009
using namespace std;
LL s1[M],s2[M],b1[M],b2[M],ans1,ans2,sum[M],ans;
int n,l,r,a[M];
int lowbit(int x){return x&(-x);} 
void add(int x)
{
    for(int i=x;i<=n;i+=lowbit(i))
    sum[i]++;
}
LL getsum(int x)
{
    LL s=0;
    for(int i=x;i>=1;i-=lowbit(i))
    s+=sum[i];
    return s;
}
LL gcd(LL x,LL y)
{
    if(!y) return x;
    return gcd(y,x%y);
}
int main()
{
    freopen("jian.in","r",stdin);
    freopen("jian.out","w",stdout);
    scanf("%d%d%d",&n,&l,&r);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        s1[i]=s1[i-1]+a[i]-l;
        s2[i]=s2[i-1]+a[i]-r;
        b1[i]=s1[i];b2[i]=s2[i];
        if(s1[i]<0) ans1++;
        if(s2[i]<=0) ans2++;
    }
    sort(s1+1,s1+n+1);
    sort(s2+1,s2+n+1);
    int t1=unique(s1+1,s1+n+1)-s1-1;
    int t2=unique(s2+1,s2+n+1)-s2-1;

    for(int i=1;i<=n;i++)
    {
        int pos;
        pos=lower_bound(s1+1,s1+t1+1,b1[i])-s1;
        b1[i]=pos;
        pos=lower_bound(s2+1,s2+t2+1,b2[i])-s2;
        b2[i]=pos;
    }
    for(int i=n;i>=1;i--)
    {
        ans1+=getsum(b1[i]);//倒着查询后面有多少个比它小的 
        add(b1[i]+1);//小于,所以要+1,后面相同的数时就不会将等于的加上了 
    }
    memset(sum,0,sizeof(sum));
    for(int i=n;i>=1;i--)
    {
        ans2+=getsum(b2[i]);
        add(b2[i]);
    }
    ans=ans2-ans1;
    LL ss=1ll*n*(n+1)/2;
    if(ss==ans) printf("1");
    else 
    {
        LL p=gcd(ans,ss);
        printf("%lld/%lld",ans/p,ss/p);
    }
    return 0;
}
  • 1
    点赞
  • 0
    评论
  • 1
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 技术工厂 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值