卡片游戏(逆序对)
题目描述
小D举办了元旦联欢活动,其中有一个卡片游戏。
游戏的规则是这样的:有n张卡片,每张卡片上正面写着一个小于等于100的正整数ai,反面都是一样的花色。这n张卡片正面朝下叠成一堆,玩这个游戏的人从中可以抽出连续的k(1≤k≤n)张卡片。如果对于这k张卡片上的数字的平均值a,满足l<=a<=r,那他就可以获得小礼物一件。
小W来玩这个游戏了,她事先通过某些途径知道了这n张卡片上写的数字,现在她想知道她获得小礼物的期望值。
小W对小数很头疼,所以请你用分数的形式告诉她答案。
输入
输入文件名为game.in
输入第1行,三个整数n,l,r。
第2行,包含n个整数ai。
输出
输出文件名为game.out
输出仅1行,表示小W获得小礼物的期望值。输出格式为“P/Q”(P和Q互质)。如果期望值是0或1就不用输出分数了
样例输入
game.in
4 2 3
3 1 2 4
game.out
7/10
样例输出
【输入输出样例2】
game.in
4 1 4
3 1 2 4
game.out
1
提示
【输入输出样例解释1】
【输入输出样例解释1】抽出的卡片 |
a(保留2位小数) |
是否满足l<=a<=r |
|
3 |
3.00 |
√ |
|
1 |
1.00 |
||
2 |
2.00 |
√ |
|
4 |
4.00 |
||
3,1 |
2.00 |
√ |
|
1,2 |
1.50 |
||
2,4 |
3.00 |
√ |
|
3,1,2 |
2.00 |
√ |
|
1,2,4 |
2.33 |
√ |
|
3,1,2,4 |
2.50 |
√ |
|
【输入输出样例解释2】
由上表得,小W总是可以获得小礼物。因此期望值是1
【数据范围】
对于30%的数据,0<n≤10,000;
对于70%的数据,0<n≤100,000;
对于100%的数据,0<n≤500,000,0<l<r≤100。
由表可得,一共有10种情况,其中有7种情况小W可以获得小礼物。因此小W获得小礼物的期望值是7/10。
solution:首先,容易想到用前缀和s[i]表示,j+1到i的平均数就是(s[i]-s[j])/(i-j) (0<=j<i<=n),由题意得,要求l<=(s[i]-s[j])/(i-j)<=r的次数,要同时满足两个约束,这个不好处理,我们可以先考虑l<=(s[i]-s[j])/(i-j)的次数,再减去(s[i]-s[j])/(i-j)<r的次数,注意,是<r不是<=r+1,这里不一样的,
先来看左边,l<=(s[i]-s[j])/(i-j) <=> l*(i-j)<=s[i]-s[j] <=> s[j]-l*j<=s[i]-l*i 这样子就比较明显了——逆序对,准确地说是顺序对,用归并排序,时间复杂度θ(n lg n)
同理,在做一个s[i]-r*i的顺序对,这个要把等于的去掉。
最好把第一个个数减去第二个个数,就是满足的次数。总次数是n*(n+1)/2,约分一下就解决了。
细节见代码:
#include<iostream> #include<cstdio> #include<cmath> #include<algorithm> #include<cstdlib> #include<cstring> using namespace std; int k1,k2; long long n,ans1,ans2,ans,fm,yf,a[1000000],s[1000000],b[1000000],c[1000000],q[1000000],ll,rr; void ms(int l,int r){ if(l==r) return; int mid=(l+r)>>1; ms(l,mid); ms(mid+1,r); k1=l; k2=mid+1; while(k1<=mid&&k2<=r){ if(b[k1]<=b[k2]){ ans1+=(long long)(r-k2+1); q[k1-l+k2-mid]=b[k1]; k1++; } else{ q[k1-l+k2-mid]=b[k2]; k2++; } } while(k1<=mid){ q[k1-l+k2-mid]=b[k1]; k1++; } while(k2<=r){ q[k1-l+k2-mid]=b[k2]; k2++; } for(int i=l;i<=r;i++) b[i]=q[i-l+1]; } void ms1(int l,int r){ if(l==r) return; int mid=(l+r)>>1; ms1(l,mid); ms1(mid+1,r); k1=l; k2=mid+1; while(k1<=mid&&k2<=r){ if(c[k1]<c[k2]){ ans2+=(long long)(r-k2+1); q[k1-l+k2-mid]=c[k1]; k1++; } else{ q[k1-l+k2-mid]=c[k2]; k2++; } } while(k1<=mid){ q[k1-l+k2-mid]=c[k1]; k1++; } while(k2<=r){ q[k1-l+k2-mid]=c[k2]; k2++; } for(int i=l;i<=r;i++) c[i]=q[i-l+1]; } long long gcd(long long x,long long y){ if(x==0) return y; return gcd(y%x,x); } int main(){ scanf("%d%lld%lld",&n,&ll,&rr); for(int i=1;i<=n;i++){ scanf("%lld",&a[i]); s[i]=s[i-1]+a[i]; b[i]=s[i]-ll*i; c[i]=s[i]-rr*i; } ms(0,n); ms1(0,n); ans=ans1-ans2; fm=n*(n+1)/2; yf=gcd(ans,fm); ans/=yf; fm/=yf; if(ans==0) printf("0\n"); else if(ans==fm) printf("1\n"); else printf("%lld/%lld\n",ans,fm); return 0; }