借鉴了一位大佬的思路: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;
}