【NOIP模拟】区间平均值

题面

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

输入格式:

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

输出格式:

输出一行一个分数 a/b 代表答案,其中 a, b 互质。 如果答案为整数则直接输出该整数即可。
样例输入 1:
4 2 3
3 1 2 4
样例输出 1:
7/10
样例输入 2:
4 1 4
3 1 2 4
样例输出 2:
1
数据规模与约定:
对于 30%的数据, 1 ≤ N ≤ 104
对于 60%的数据, 1 ≤ N ≤ 105
对于 100%的数据, 1 ≤ N ≤ 5 × 105 ,0 < l ≤ r ≤ 100。

分析

 求平均数在[l,r]等价于求平均数在[1,l)和[1,r]的区间数量,后者减去前者即答案

怎么维护??

以区间[i,k]≤r的平均数为例,如果平均数比r小,那么每个数减去r后求和的值一定≤0

即 a[i]-r+a[i+1]-r+a[i+2]-r+·······+a[i+k-1]-r≤0

r是常数,假设b[i]=a[i]-r,则b[i]+b[i+1]+······+b[i+k-1]≤0

看到b数组在求和,很容易联想到维护b数组的前缀和s[]

b[i]+b[i+1]+······+b[i+k-1]=s[i+k-1]-s[i-1].可得s[i+k-1]-s[i-1]≤0

所以,s[i+k-1]≤s[i-1],又因为i-1≤i+k-1,序号更靠前,值却更大,有多少个平均数≤r的区间,就是s[]序列的逆序对有多少对,对于l同理,只不过≤需要改为<。

所以我们只需要求出两组逆序对,就求出了两个区间的数量,再相减。

防止数组编号为负以及数字过大,用了基础的离散化。还需要注意开闭区间的问题,l是开区间,是求严格的逆序对,r是开区间,是求不严格的(可以包括本身)

#include
using namespace std;
#define N 600000
#define ll long long
ll n,l,r,fz,fm,nx1,nx2,mx1,mx2,gcd;
ll a[N],b[N],c[N],d[N],sum1[N],sum2[N],g[N];
inline ll lowbit(ll x)
{
    return x&(-x);
}
inline void add(ll x)
{
    for(;x<=n;x+=lowbit(x))
        g[x]++;
}
inline ll query(ll x)
{
    ll ret=0;
    for(;x;x-=lowbit(x))
        ret+=g[x];
    return ret;
}

int main()
{
    scanf("%lld%lld%lld",&n,&l,&r);
    for(ll i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        sum1[i]=sum1[i-1]+a[i]-r;
        sum2[i]=sum2[i-1]+a[i]-l;
        c[i]=sum1[i];d[i]=sum2[i];
        if(sum1[i]<=0)nx1++;//与s[0]比较 ,区间[1,i]的平均值<=r 
        if(sum2[i]<0)nx2++;//与 s[0]比较,区间[1,i]的平均值
    }
    sort(c+1,c+1+n);sort(d+1,d+1+n);
    mx1=unique(c+1,c+1+n)-(c+1);mx2=unique(d+1,d+1+n)-(d+1);
    for(ll i=1;i<=n;i++)
    {
        sum1[i]=lower_bound(c+1,c+1+mx1,sum1[i])-c;
        sum2[i]=lower_bound(d+1,d+1+mx2,sum2[i])-d;
    }
    for(ll i=n;i;i--)
    {
        nx1+=query(sum1[i]);//不严格的逆序对 
        add(sum1[i]);
    }
    memset(g,0,sizeof(g));
    for(ll i=n;i;i--)
    {
        nx2+=query(sum2[i]-1);
        add(sum2[i]); 
    }
    fz=nx1-nx2;fm=(1+n)*n/2;gcd=__gcd(fz,fm);
    if(fz==fm)printf("1");
    else printf("%lld/%lld",fz/gcd,fm/gcd);
}

 

转载于:https://www.cnblogs.com/NSD-email0820/p/9549434.html

你可能感兴趣的:(【NOIP模拟】区间平均值)