题目链接:https://cn.vjudge.net/problem/HDU-5145
题意:区间[l, r] 内的数全排列能得到多少种不同的排列
题解:假设sum是当前的数量,再加上一个就是乘上sum+1,乘上这个数+1 的数量的逆元,减去一个是乘上sum的逆元,乘上这个数的数量
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define lowbit(x) x&(-x)
const int N=30010;
const ll mod=1e9+7;
struct node{
int l,r;
int id;
}a[N];
int n,m,CM;
int c[N];
ll num[N];
ll ans[N];
ll inv[N];
bool cmp(node x,node y)
{
if(x.l/CM!=y.l/CM) return x.l<y.l;
else return x.r<y.r;
}
ll ksm(ll x, ll y)
{
ll res=1;
while(y)
{
if(y&1) res=res*x%mod;
x=x*x%mod;
y>>=1;
}
return res;
}
int main()
{
for(int i=1;i<=30000;i++)
{
inv[i]=ksm(1LL*i,mod-2);
}
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&c[i]);
CM=(int)sqrt(n);
for(int i=1;i<=m;i++)scanf("%d%d",&a[i].l,&a[i].r),a[i].id=i;
sort(a+1,a+1+m,cmp);
int l,r;
ll res,sum;
for(int i=1,j=1;j<=m;i++)
{
memset(num,0,sizeof(num));
l=a[j].l+1,r=a[j].l;
res=1;
sum=0;
for(;j<i*CM && j<=m;j++)
{
while(a[j].l<l)
{
l--;
sum++;
if(sum>0)res=res*sum%mod;
num[c[l]]++;
if(num[c[l]]>0)res=res*inv[num[c[l]]]%mod;
}
while(a[j].l>l)
{
if(sum>0)res=res*inv[sum]%mod;
if(num[c[l]]>0)res=res*num[c[l]]%mod;
sum--;
num[c[l]]--;
l++;
}
while(a[j].r<r)
{
if(sum>0)res=res*inv[sum]%mod;
if(num[c[r]]>0)res=res*num[c[r]]%mod;
sum--;
num[c[r]]--;
r--;
}
while(a[j].r>r)
{
r++;
sum++;
if(sum>0)res=res*sum%mod;
num[c[r]]++;
if(num[c[r]]>0)res=res*inv[num[c[r]]]%mod;
// cout<<sum<<" "<<res<<" "<<inv[num[c[r]]]<<" "<<c[r]<<" "<<num[c[r]]<<endl;
}
// cout<<a[j].id<<" "<<res<<endl;
ans[a[j].id]=res;
l=a[j].l,r=a[j].r;
}
}
for(int i=1;i<=m;i++)
{
printf("%lld\n",ans[i]);
}
}
return 0;
}