BZOJ2038 [2009国家集训队]小Z的袜子(hose)

题意

F.A.Qs Home Discuss ProblemSet Status Ranklist Contest 入门OJ ModifyUser   autoint Logout 捐赠本站
Problem 2038. -- [2009国家集训队]小Z的袜子(hose)

2038: [2009国家集训队]小Z的袜子(hose)

Time Limit: 20 Sec   Memory Limit: 259 MB
Submit: 17453   Solved: 8025
[ Submit][ Status][ Discuss]

Description

作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿。终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命……
具体来说,小Z把这N只袜子从1N编号,然后从编号LR(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。

HINT

Source

[ Submit][ Status][ Discuss]

HOME Back

分析

对询问分块,先按照左端点排序,然后以\(\sqrt M\)个询问为一块,块内按右端点排序。

这样处理的时候块内相邻两个询问的左端点变化在\(\sqrt N\)以内,右端点变化单调。所以总时间复杂度是\(O(N \sqrt N)\)

对答案,只需维护
\[ \sum_c \frac{num[c]*(num[c]-1)}2 \]
即可,端点移动时可以\(O(1)\)维护。

算是学了一种新奇的莫队写法。

代码

#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
    rg T data=0,w=1;rg char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') w=-1;ch=getchar();}
    while(isdigit(ch)) data=data*10+ch-'0',ch=getchar();
    return data*w;
}
template<class T>il T read(rg T&x) {return x=read<T>();}
typedef long long ll;
using namespace std;

co int N=5e4+1;
int n,m,c[N],L[N],R[N],num[N];
struct P{int id,l,r;}p[N];
ll ans,Ans[N][2];
bool cmpl(co P&a,co P&b) {return a.l<b.l;}
bool cmpr(co P&a,co P&b) {return a.r<b.r;}
void work(int x,int w){
    ans-=(ll)num[x]*(num[x]-1);
    num[x]+=w;
    ans+=(ll)num[x]*(num[x]-1);
}
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
int main(){
//  freopen(".in","r",stdin),freopen(".out","w",stdout);
    read(n),read(m);
    for(int i=1;i<=n;++i) read(c[i]);
    for(int i=1;i<=m;++i) read(p[i].l),read(p[i].r),p[i].id=i;
    sort(p+1,p+m+1,cmpl);
    int t=sqrt(m);
    for(int i=1;i<=t;++i) L[i]=R[i-1]+1,R[i]=i*t;
    if(R[t]<m) L[t+1]=R[t]+1,R[++t]=m;
    for(int i=1;i<=t;++i){
        sort(p+L[i],p+R[i]+1,cmpr);
        fill(num+1,num+n+1,0);
        ans=0;
        int l=p[L[i]].l,r=p[L[i]].r;
        for(int j=l;j<=r;++j) work(c[j],1);
        Ans[p[L[i]].id][0]=ans;
        Ans[p[L[i]].id][1]=(ll)(r-l)*(r-l+1);
        for(int j=L[i]+1;j<=R[i];++j){
            while(r<p[j].r) work(c[++r],1);
            while(r>p[j].r) work(c[r--],-1);
            while(l<p[j].l) work(c[l++],-1);
            while(l>p[j].l) work(c[--l],1);
            Ans[p[j].id][0]=ans;
            Ans[p[j].id][1]=(ll)(r-l)*(r-l+1);
        }
    }
    for(int i=1;i<=m;++i){
        ll g=gcd(Ans[i][0],Ans[i][1]);
        if(!g) Ans[i][1]=1;
        else Ans[i][0]/=g,Ans[i][1]/=g;
        printf("%lld/%lld\n",Ans[i][0],Ans[i][1]);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/autoint/p/10620560.html