卡片游戏(逆序对)

题目描述

小D举办了元旦联欢活动,其中有一个卡片游戏。

游戏的规则是这样的:有n张卡片,每张卡片上正面写着一个小于等于100的正整数ai,反面都是一样的花色。这n张卡片正面朝下叠成一堆,玩这个游戏的人从中可以抽出连续的k(1≤kn)张卡片。如果对于这k张卡片上的数字的平均值a,满足l<=a<=r,那他就可以获得小礼物一件。

小W来玩这个游戏了,她事先通过某些途径知道了这n张卡片上写的数字,现在她想知道她获得小礼物的期望值。

小W对小数很头疼,所以请你用分数的形式告诉她答案。

输入

输入文件名为game.in

输入第1行,三个整数nlr

第2行,包含n个整数ai

输出

输出文件名为game.out

输出仅1行,表示小W获得小礼物的期望值。输出格式为“P/Q”(PQ互质)。如果期望值是01就不用输出分数了

样例输入

game.in 	 
4 2 3 
3 1 2 4 	
game.out
7/10

样例输出

【输入输出样例2】
game.in 	 
4 1 
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;  
} 


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值