首页 > 代码库 > [BZOJ 2038][2009国家集训队]小Z的袜子(hose)(莫队)

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

Description

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

Solution

把询问按照左端点排序,块内按照右端点排序;

(l,r)的答案转移到(l+1,r)/(l-1,r)/(l,r+1)/(l,r-1)只需要O(1)的时间,所以总的时间复杂度是O(n^1.5)【黄学长blog有时间复杂度的分析 hzwer】

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<cmath>#include<algorithm>#define MAXN 50005typedef long long LL;using namespace std;int n,m,unit,col[MAXN],num[MAXN];LL ans=0;struct Node{    int l,r,id;    LL a,b;}q[MAXN];int Read(){    int x=0,f=1;char c=getchar();    while(c<0||c>9){        if(c==-)f=-1;c=getchar();    }    while(c>=0&&c<=9){        x=x*10+c-0;c=getchar();    }    return x*f;}bool cmp(Node A,Node B){    if(A.l/unit!=B.l/unit)    return A.l/unit<B.l/unit;    return A.r<B.r;}bool cmp_id(Node A,Node B){    return A.id<B.id;}LL gcd(LL a,LL b){    if(!b)return a;    return gcd(b,a%b);}void update(int x,int add){    ans-=num[col[x]]*(num[col[x]]-1);    num[col[x]]+=add;    ans+=num[col[x]]*(num[col[x]]-1);}void work(){    int L=1,R=0;    for(int i=0;i<m;i++)    {        while(q[i].l<L)update(L-1,1),L--;        while(q[i].l>L)update(L,-1),L++;        while(q[i].r<R)update(R,-1),R--;        while(q[i].r>R)update(R+1,1),R++;        if(q[i].l==q[i].r){q[i].a=0,q[i].b=1;continue;}        q[i].a=ans;        q[i].b=(LL)(R-L+1)*(R-L);        LL k=gcd(q[i].a,q[i].b);        q[i].a/=k,q[i].b/=k;    }}int main(){    n=Read();m=Read();    unit=sqrt(n);    for(int i=0;i<n;i++)    col[i]=Read();    for(int i=0;i<m;i++)    {        q[i].l=Read()-1;        q[i].r=Read()-1;        q[i].id=i;    }    sort(q,q+m,cmp);    work();    sort(q,q+m,cmp_id);    for(int i=0;i<m;i++)    {        printf("%lld/%lld\n",q[i].a,q[i].b);    }    return 0;} 

 

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