hdu5145(莫队算法+组合数逆元)

版权声明:本人蒟蒻,如有大佬转发,感激不尽,带上我的博客链接即可。 https://blog.csdn.net/qq_36300700/article/details/81540786

借鉴了一位大佬的思路:https://www.cnblogs.com/HDUjackyan/p/8996172.html

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5145

题意:一个人有n个女朋友,每个女朋友都有一个班级a[i],现有m个询问。每个询问有一个区间范围[L,R],表示这个人想要约[L,R]范围内的女生有几种约法。(第L个女生到第R个女生的区间,而不是班级).

分析:
给出公式,对于范围[L,R]来说可能的情况为(R-L+1)!/【(num[x1]!)(num[x2]!)……(num[xn]!)】 ,x1,x2,……,xn为[L,R]区间内出现过的所有不相同的数,num[]表示该数出现的次数

即情况数=该区间范围内所有数的全排列/每个数各自的全排列的求和。

因为出现取余操作,所以当出现除法操作时需要用到逆元(又因为mod=1e9+7为一个质数,所以考虑用费马小定理)

因为有很多阶乘操作,所以通过预处理得到可能范围内所以数的阶乘(保存在d[i]中)和阶乘的逆元(保存在nd[i]中)

因为答案的分子(即(R-L+1)!可在预处理后直接访问得到),所以在中间访问过程中只记录分母(记作sum)

当num[a[p]]]++时,需要sum*=num[a[p]];当num[a[p]]–时,需要sum/=num[a[p]];

ac代码:

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <queue>
#include <vector>
#include <map>

using namespace std;

#define ll long long

const int maxn =30000+10;
const ll mod =1000000007;
ll a[maxn],num[maxn],sum,ans[maxn];
ll fac[maxn]={1,1},f[maxn]={1,1};
ll len;

void init()
{
    for(int i=2; i<=maxn-1; i++)
    {
        fac[i]=fac[i-1]*i%mod;//阶乘
      f[i]=(mod-mod/i)*f[mod%i]%mod;//逆元
    }


}

 ll quick(ll x,ll y)
 {
     ll sum=1;
     while ( y ) {
         if ( y&1 ) sum=(sum*x)%mod;
         x=(x*x)%mod;
         y/=2;
     }
     return sum%mod;
 }



struct Query
{
    ll L,R,block,ans;
    int id;
}q[maxn];

bool cmp(Query a, Query b)
{
        if(a.block==b.block)
            return a.R<b.R;
        return a.block <b.block;
}

void updata(int pos, int flag)
{
    int i=a[pos];

    if(flag)
    {
        num[i]++;
        sum=sum*num[i]%mod;
    }
    else
    {
        sum=sum*f[num[i]]%mod;
        num[i]--;
    }
}

int main()
{
    init();

    long long T,n,m;
    scanf("%lld",&T);
    while(T--)
    {
        memset(num,0,sizeof(num));
        scanf("%lld %lld",&n,&m);
        len =sqrt(n+0.0);
        for(int i=1; i<=n; i++)
            scanf("%lld",&a[i]);

        for(int i=1; i<=m; i++)
        {
            ll l,r;
            scanf("%lld %lld",&l,&r);
           q[i].L=l,q[i].R=r,q[i].id=i,q[i].block=(l-1)/len+1;
        }

        sort(q+1,q+m+1,cmp);

        int l=1,r=1;
        num[a[1]]=1;
        sum=1;

        for(int i=1; i<=m; i++)
        {
            while(r<q[i].R)
            {
                r++;
                updata(r,1);
            }

            while(l>q[i].L)
            {
                l--;
                updata(l,1);
            }

            while(l<q[i].L)
            {
                updata(l,0);
                l++;
            }

            while(r>q[i].R)
            {
                updata(r,0);
                r--;
            }

            ans[q[i].id]=fac[r-l+1]*quick(sum,mod-2)%mod;
        }
        for(int i=1; i<=m; i++)
            printf("%lld\n",ans[i]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_36300700/article/details/81540786