jian

本文介绍了一种通过求解逆序对数量的方法来计算给定数值区间内平均值落在特定范围的概率。具体步骤包括预处理数据、计算前缀和、进行排序及查找,并最终利用树状数组计算逆序对数,得出概率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【问题描述】
有N个数,随机选择一段区间,如果这段区间的所有数的平均值在[m,r]中则你比较厉害
求你比较厉害的概率
【输入格式】
第一行有三个数N,m,r,含义如上描述
接下来一行有N个数代表每一个数的值
【输出格式】
输出一行一个分数
如果答案为整数则直接输出该整数即可
【样例输入 1】
4 2 3
3 1 2 4
【样例输出 1】
7/10
【样例输入 2】
4 1 4
3 1 2 4
【样例输出 2】
1
【样例解释】
塔外面有棵树

【数据规模与约定】
60%的数据,1 ≤ N≤ 105
对于100%的数据,1 ≤ N ≤ 5× 105 ,0 < m≤ 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数组逆序对数

引用结束

树状数组求逆序对
因为有负数
做了一点特殊处理
使它对应一个正数

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<queue>
#include<vector>
#include<climits>
#include<string>
#include<cstdlib>
#include<ctime>
#define MOD 100000007
#define LL long long
using namespace std;

LL n,l,r,num[500005],s1[500005],s2[500005],b1[500005],b2[500005],pos,t1,t2,ans1,ans2,p,p1,p2,tmp,c[500005];

LL lowbit(LL x)
{
    return x&(-x);
}

LL gcd(LL a,LL b)
{
    if(!a) return b;
    else return gcd(b%a,a); 
}

LL getnum(LL x)
{
    LL ans=0,i;

    for(i=x;i>=1;i-=lowbit(i))
        ans+=c[i];

    return ans;
}

void add(LL x)
{
    LL i;
    for(i=x;i<=n;i+=lowbit(i)) c[i]++;
}

int main()
{
    LL i;

    scanf("%lld%lld%lld",&n,&l,&r);

    for(i=1;i<=n;i++)
    {
        scanf("%lld",&num[i]);

        s1[i]=num[i]-l+s1[i-1];
        b1[i]=s1[i];
        if(s1[i]<0) ans1++;

        s2[i]=num[i]-r+s2[i-1];
        b2[i]=s2[i];
        if(s2[i]<=0) ans2++;    
    }

    sort(s1+1,s1+n+1);
    sort(s2+1,s2+n+1);
    t1=unique(s1+1,s1+n+1)-s1-1;
    t2=unique(s2+1,s2+n+1)-s2-1;

    for(i=1;i<=n;i++)
    {
        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(i=n;i>=1;i--)
    {
        ans1+=getnum(b1[i]);
        add(b1[i]+1);
    }

    memset(c,0,sizeof(c));

    for(i=n;i>=1;i--)
    {
        ans2+=getnum(b2[i]);
        add(b2[i]); 
    }

    p1=ans2-ans1;
    p2=(n*(n+1))/2;
    tmp=gcd(p1,p2);

    p1/=tmp;
    p2/=tmp;

    if(p1==p2) printf("1");
    else printf("%lld/%lld",p1,p2);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值