题面
有 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是开区间,是求不严格的(可以包括本身)
#includeusing 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); }