HYSBZ 2038 小Z的袜子(hose) (莫队算法入门)

题意:取一段区间,求区间中任取两个数相同的概率;

思路:所求概率P=(A*(A-1)/2+B*(B-1)/2+......)/(R-L+1)*(R-L)/2化简得P=(A*A+B*B+......+Z*Z-(R-L+1))/(R-L+1)*(R-L);

        将询问区间左端点放在同一分块中处理,每次处理一个块中的所有询问,对于同一块,询问右端点按严格递增处理,左端点不断移动;

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int t,n,m,unit;
int a[500010],num[500010];
struct node
{
    int id,L,R;
}M[500010];
int cmp(node a,node b)
{
    if(a.L/unit!=b.L/unit) return a.L/unit<b.L/unit;
    return a.R<b.R;
}
long long gcd(long long a,long long b)
{
    if(b==0) return a;
    return gcd(b,a%b);
}
struct que
{
    long long a,b;
    void reduce() //分数约分
    {
        long long d=gcd(a,b);
        a/=d;b/=d;
    }
}ans[500010];
void work()
{
    int L=1,R=0;
    long long temp=0;
    memset(num,0,sizeof(num)); //区间中同种数的个数
    for(int i=0;i<m;i++)
    {
        while(R<M[i].R)
        {
            R++;
            temp-=(long long)num[a[R]]*num[a[R]];
            num[a[R]]++;//a[R]的个数加1
            temp+=(long long)num[a[R]]*num[a[R]];
        }
        while(L>M[i].L)
        {
            L--;
            temp-=(long long)num[a[L]]*num[a[L]];
            num[a[L]]++;//a[L]的个数加1
            temp+=(long long)num[a[L]]*num[a[L]];
        }
        while(R>M[i].R)
        {
            temp-=(long long)num[a[R]]*num[a[R]];
            num[a[R]]--;//a[L]的个数减1
            temp+=(long long)num[a[R]]*num[a[R]];
            R--;
        }
        while(L<M[i].L)
        {
            temp-=(long long)num[a[L]]*num[a[L]];
            num[a[L]]--;//a[R]的个数减1
            temp+=(long long)num[a[L]]*num[a[L]];
            L++;
        }
        ans[M[i].id].a=temp-(R-L+1);  
        ans[M[i].id].b=(long long)(R-L)*(R-L+1);
        ans[M[i].id].reduce();
    }
}
int main()
{
    int i,j,k;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(i=1;i<=n;i++)
            scanf("%d",&a[i]);
        unit=(int)sqrt(n); //总区间分为unit块
        for(i=0;i<m;i++)
        {
            M[i].id=i;
            scanf("%d%d",&M[i].L,&M[i].R);
        }
        sort(M,M+m,cmp); //对询问排序
        work();
        for(i=0;i<m;i++)
        printf("%lld/%lld
",ans[i].a,ans[i].b);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/dashuzhilin/p/4651301.html