http://acm.hdu.edu.cn/showproblem.php?pid=5145 (题目连接)
给出一个数列,每次求一个区间数字的非重排列数量。答案对1e9+7取模。
每次往里加入一个新的数字或者减去一个新的数字,前后的排列数目是可以通过乘除转移。答案要求取模,在用除法的时候要用到逆元,需要离线处理。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=30000;
const ll mod=1e9+7;
int t,n,m,a[maxn+50],T;
ll c[maxn+50],an[maxn+50];
struct node{
int l,r,id;
}s[maxn+50];
bool cmp(const node &a,const node &b)
{
if(a.l/t==b.l/t) return a.r>b.r;
return a.l<b.l;
}
ll qmod(ll n,ll p)
{
ll ans=1;
while(p)
{
if(p&1) ans=ans*n%mod;
p>>=1;
n=n*n%mod;
}
return ans;
}
ll inv[maxn+50];
void init()
{
for(int i=0;i<=maxn;i++)
inv[i]=qmod(1ll*i,mod-2);
}
int main()
{
init();
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&s[i].l,&s[i].r);
s[i].id=i;
}
t=(int)(sqrt(n)+0.5);
sort(s+1,s+m+1,cmp);
int l=1,r=0;
memset(c,0,sizeof c);
ll ans=1,sum=0;
for(int i=1;i<=m;i++)
{
while(l<s[i].l)
{
if(sum>0)
ans=ans*inv[sum]%mod;
if(c[a[l]]>0)
ans=ans*c[a[l]]%mod;
c[a[l]]--;
sum--;
l++;
}
while(l>s[i].l)
{
l--;
c[a[l]]++;
sum++;
if(sum>0)
ans=ans*sum%mod;
if(c[a[l]]>0)
ans=ans*inv[c[a[l]]]%mod;
}
while(r>s[i].r)
{
if(sum>0)
ans=ans*inv[sum]%mod;
if(c[a[r]]>0)
ans=ans*c[a[r]]%mod;
c[a[r]]--;
sum--;
r--;
}
while(r<s[i].r)
{
r++;
c[a[r]]++;
sum++;
if(sum>0)
ans=ans*sum%mod;
if(c[a[r]]>0)
ans=ans*inv[c[a[r]]]%mod;
}
an[s[i].id]=ans;
}
for(int i=1;i<=m;i++)
printf("%lld\n",an[i]);
}
return 0;
}