一道神题

---恢复内容开始---

给一个长为N的序列,对与一个区间,如果其平均值在【L,R】内,你比较厉害。

求你比较厉害的概率。

我只做了35分,但看了别人的神代码,我终于领会到了代码的神奇。

思路:

  我们先求平均值在【0,R】,和【0,l)的区间个数。

  令a[i]=a[i]-R;  s[i]为a[i]的前i项的和,那么s[i]的增减就代表了 某个区间,是否是厉害区间。(增代表不是,减或者连续的减,都代表了这一段区间是厉害区间)

  然后查找逆序对的个数,因为区间平均值的 值域是很小的 对于每个值都有 很多的重复,这就用到了离散化(因为只需要知道其相对位置就行了)。

    这里用到了unque去重函数

  

 

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define LL long long 
#define N 500010
LL n,l,r;
LL ans1,ans2,a[N],b1[N],b2[N],c[N],t,ans,tot;
LL s1[N],s2[N];
#define lowbit(i) (i&(-i))
void gcd()
{
    long long a,b,c;
    a=ans,b=tot;
    while(b)
    {
        c=a%b;
        a=b;
        b=c;
    }
    t=a;
}
void Add(int x)
{
    for(int i=x;i<=n;i+=lowbit(i))        c[i]++;
}
LL Get(int x)
{
    LL ans=0;
    for(int i=x;i>=1;i-=lowbit(i))
        ans+=c[i];
    return ans;
}
int main()
{
    freopen("jian.in","r",stdin);
    freopen("jian.out","w",stdout);
    scanf("%d%d%d",&n,&l,&r);
    for(int i=1;i<=n;i++)    
        scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)
    {
        s1[i]=s1[i-1]+a[i]-l;
        b1[i]=s1[i];
        if(s1[i]<0) ans1++;
        s2[i]=s2[i-1]+a[i]-r;
        b2[i]=s2[i];
        if(s2[i]<=0)    ans2++;
    }
    
    sort(s1+1,s1+1+n);
    sort(s2+1,s2+n+1);
    LL t1=unique(s1+1,s1+1+n)-s1-1;
    LL t2=unique(s2+1,s2+1+n)-s2-1;
    for(int i=1;i<=n;i++)
    {
        LL pos=lower_bound(s1+1,s1+1+t1,b1[i])-s1;
        b1[i]=pos;
        pos=lower_bound(s2+1,s2+1+t2,b2[i])-s2;
        b2[i]=pos;
    }
    for(int i=n;i>=1;i--)
    {
        ans1+=Get(b1[i]);
        Add(b1[i]+1);
    }
    memset(c,0,sizeof c);
    for(int i=n;i>=1;i--)
    {
        ans2+=Get(b2[i]);
        Add(b2[i]);
    }
    ans=ans2-ans1; 
    tot=(n+1)*n/2;
    if(ans==tot)
    {
        cout<<1;
        return 0;
    } 
    if(ans==0)
    {
        cout<<0;
        return 0;
    }
    gcd();
    printf("%lld/%lld",ans/t,tot/t);
    return 0;
}

 

---恢复内容结束---

posted @ 2017-10-06 19:06  浪矢-CL  阅读(172)  评论(0编辑  收藏  举报