【莫队算法】[HDU5145]NPY and girls
题目
题目大意:求区间内可重复全排列的个数
分析:如果S是一个多重集,它有K个不同的类型元素,各元素分别为n1,n2,…,nk个,那么,S的r排列个数为n! / (n1!n2!…*nr!)。
所以当我们知道区间[l,r]的答案求[l,r+1]的答案时,ans[l][r+1]=ans[l][r]*((r+1)-l+1)/cnt[a[r+1]];
由于模运算不支持除法,可以用乘以这个数的逆元来代替除法。
同时因为MOD是质数,可以用费马小定理快速求乘法逆元。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
#define MAXN 30000
#define MOD 1000000007
int n,m,block[MAXN+10],inv[MAXN+10],T,cnt[MAXN+10],ansq[MAXN+10],a[MAXN+10];
void Read(int &x){
char c;
while(c=getchar(),c!=EOF)
if(c>='0'&&c<='9'){
x=c-'0';
while(c=getchar(),c>='0'&&c<='9')
x=x*10+c-'0';
ungetc(c,stdin);
return;
}
}
struct node{
int l,r,pos;
bool operator<(const node &x)const{
if(block[l]==block[x.l])
return r<x.r;
return block[l]<block[x.l];
}
}query[MAXN+10];
int quick_pow(int a,int b){
int ret=1;
while(b){
if(b&1)
ret=1ll*ret*a%MOD;
a=1ll*a*a%MOD;
b>>=1;
}
return ret;
}
void prepare(){
for(int i=1;i<=MAXN;i++)
inv[i]=quick_pow(i,MOD-2);
}
void init(){
Read(n),Read(m);
int i,t=sqrt(n+0.5);
for(i=1;i<=n;i++){
block[i]=(i+t-1)/t;
Read(a[i]);
}
node *p;
for(i=1;i<=m;i++){
p=&query[i];
Read(p->l),Read(p->r);
p->pos=i;
}
sort(query+1,query+m+1);
}
void solve(){
int i,l=1,r=0,ans=1;
node *p;
for(i=1;i<=m;i++){
p=&query[i];
while(r<p->r){
cnt[a[++r]]++;
ans=1ll*ans*(r-l+1)%MOD*inv[cnt[a[r]]]%MOD;
}
while(r>p->r){
ans=1ll*ans*inv[r-l+1]%MOD*cnt[a[r]]%MOD;
cnt[a[r--]]--;
}
while(l>p->l){
cnt[a[--l]]++;
ans=1ll*ans*(r-l+1)%MOD*inv[cnt[a[l]]]%MOD;
}
while(l<p->l){
ans=1ll*ans*inv[r-l+1]%MOD*cnt[a[l]]%MOD;
cnt[a[l++]]--;
}
ansq[p->pos]=ans;
}
}
void print(){
for(int i=1;i<=m;i++)
printf("%d\n",ansq[i]);
}
int main()
{
prepare();
Read(T);
while(T--){
memset(cnt,0,sizeof cnt);
init();
solve();
print();
}
}