BZOJ 2038 [2009国家集训队]小Z的袜子【莫队】

Time Limit: 20 Sec
Memory Limit: 259 MB

Description

作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿。终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命……
具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两只袜子是不是完整的一双,甚至不在意两只袜子是否一左一右,他却很在意袜子的颜色,毕竟穿两只不同色的袜子会很尴尬。
你的任务便是告诉小Z,他有多大的概率抽到两只颜色相同的袜子。当然,小Z希望这个概率尽量高,所以他可能会询问多个(L,R)以方便自己选择。

Input

输入文件第一行包含两个正整数N和M。N为袜子的数量,M为小Z所提的询问的数量。接下来一行包含N个正整数Ci,其中Ci表示第i只袜子的颜色,相同的颜色用相同的数字表示。再接下来M行,每行两个正整数L,R表示一个询问。

Output

包含M行,对于每个询问在一行中输出分数A/B表示从该询问的区间[L,R]中随机抽出两只袜子颜色相同的概率。若该概率为0则输出0/1,否则输出的A/B必须为最简分数。(详见样例)

Sample Input

6 4
1 2 3 3 3 2
2 6
1 3
3 5
1 6

Sample Output

2/5
0/1
1/1
4/15

【样例解释】

询问1:共C(5,2)=10种可能,其中抽出两个2有1种可能,抽出两个3有3种可能,概率为(1+3)/10=4/10=2/5。
询问2:共C(3,2)=3种可能,无法抽到颜色相同的袜子,概率为0/3=0/1。
询问3:共C(3,2)=3种可能,均为抽出两个3,概率为3/3=1/1。
注:上述C(a, b)表示组合数,组合数C(a, b)等价于在a个不同的物品中选取b个的选取方案数。

【数据规模和约定】

30%的数据中 N,M ≤ 5000;
60%的数据中 N,M ≤ 25000;
100%的数据中 N,M ≤ 50000,1 ≤ L < R ≤ N,Ci ≤ N。


题目分析:

假设查询区间长度为len
对于第i种袜子,在此区间内共有cnt[i]个

那么第一只抽中 i 的机率为 c n t [ i ] l e n
紧接着第二只抽中 i 的机率为 c n t [ i ] 1 l e n 1
那么在这个区间内抽中一双 i 的机率为 c n t [ i ] 2 c n t [ i ] l e n 2 l e n

对于区间内所有不同袜子
能抽到一双相同的几率为 i = 1 N c n t [ i ] 2 c n t [ i ] l e n 2 l e n

由于区间内所有 c n t [ i ] 相加必定等于len
所以我们要维护的就是区间内所有cnt[i]的平方和

关于如何维护区间平方和
洛谷P2709 小B的询问【莫队】题解


#include<iostream>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long lt;

lt read()
{
    lt f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return x*f;
}

const int maxn=50010;
int n,m;
lt a[maxn],cnt[maxn],t;
lt L=1,R=0,sum;
struct node{int ll,rr,num;}q[maxn];
struct node2{lt r1,r2;}ans[maxn];
bool cmp(node a,node b){return (a.ll/t)==(b.ll/t) ?a.rr<b.rr :(a.ll/t)<(b.ll/t);}
lt gcd(lt a,lt b){return b==0 ?a:gcd(b,a%b);}

void add(int x)
{
    sum+=cnt[x]<<1|1;
    cnt[x]++;
}

void del(int x)
{
    sum-=(cnt[x]<<1)-1;
    cnt[x]--;
}

int main()
{
    n=read();m=read(); t=sqrt(n);
    for(int i=1;i<=n;++i) a[i]=read();
    for(int i=1;i<=m;++i)
    q[i].ll=read(),q[i].rr=read(),q[i].num=i;

    sort(q+1,q+1+m,cmp);
    for(int i=1;i<=m;++i)
    {
        while(R<q[i].rr) add(a[++R]);
        while(R>q[i].rr) del(a[R--]);
        while(L<q[i].ll) del(a[L++]);
        while(L>q[i].ll) add(a[--L]);
        lt len=q[i].rr-q[i].ll+1;
        lt r1=sum-len,r2=len*len-len;//分子和分母
        if(r1==0) ans[q[i].num].r1=0,ans[q[i].num].r2=1;//几率为0的情况
        else
        {
            lt g=gcd(r2,r1);//约分
            ans[q[i].num].r1=r1/g; ans[q[i].num].r2=r2/g;
        }
    }
    for(int i=1;i<=m;++i)
    printf("%lld/%lld\n",ans[i].r1,ans[i].r2);
    return 0;
}


猜你喜欢

转载自blog.csdn.net/niiick/article/details/80336169