离线处理加莫队算法,再就是找转移的规律,进行处理,莫队算法我的理解就是将查询分格化,用的上限复杂度是nsqrtn,莫队算法在处理左右区间查询时,有很大的作用。自己画画图会更容易理解。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<string>
#include<cmath>
#include<algorithm>
#define LL long long
using namespace std;
const int maxn = 30010;
const int mod= 1000000007;
int T,nn,m,c[maxn],sum[maxn],sqrtn;
struct node
{
int x,y,i,ans;
} n[maxn];
int cmp(node a,node b) //莫队算法
{
if(a.y/sqrtn==b.y/sqrtn)
return a.x/sqrtn<b.x/sqrtn;
return a.y/sqrtn<b.y/sqrtn;
}
int comp(node a,node b)
{
return a.i<b.i;
}
int exgcd(int a,int b, int &x,int &y) //ax+by=gcd(a,b)可用于求逆元
{
int d=a;
if(b!=0)
{
d=exgcd(b,a%b,y,x);
y-=(a/b)*x;
}
else
{
x=1,y=0;
}
return d;
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d %d",&nn,&m);
for(int i=1; i<=nn; i++)scanf("%d",&c[i]);
sqrtn=(int)sqrt(nn);
for(int i=0; i<m; i++)scanf("%d %d",&n[i].x,&n[i].y),n[i].i=i;
sort(n,n+m,cmp);
memset(sum,0,sizeof(sum));
int x=1,y=0;
LL ans=1;
for(int i=0; i<m; i++)
{
while(y<n[i].y)
{
y++;
sum[c[y]]++;
int a,b;
exgcd(sum[c[y]],mod,a,b);
if(a<0)a+=mod;
ans=(ans*(LL)(y-x+1)%mod)%mod;
ans=(ans*(LL)a)%mod;
}
while(x>n[i].x)
{
x--;
sum[c[x]]++;
int a,b;
exgcd(sum[c[x]],mod,a,b);
if(a<0)a+=mod;
ans=(ans*(LL)(y-x+1)%mod)%mod;
ans=(ans*(LL)a)%mod;
}
while(x<n[i].x)
{
int a,b;
exgcd(y-x+1,mod,a,b);
if(a<0)a+=mod;
ans=(ans*(LL)(sum[c[x]])%mod)%mod;
ans=(ans*(LL)a)%mod;
sum[c[x]]--;
x++;
}
while(y>n[i].y)
{
int a,b;
exgcd(y-x+1,mod,a,b);
if(a<0)a+=mod;
ans=(ans*(LL)sum[c[y]]%mod)%mod;
ans=(ans*(LL)a)%mod;
sum[c[y]]--;
y--;
}
n[i].ans=ans;
}
sort(n,n+m,comp);
for(int i=0; i<m; i++)
printf("%d\n",n[i].ans);
}
return 0;
}