一个很普通的莫队题+可重集排列
莫队的说明网上已经很多了,我就不献丑了
直接来考虑添加端点或者删除端点的情况吧
添加 就是分子乘区间长度,分母乘当前位置的数的个数,然后更新数的个数
删除 就是分母乘区间长度,分子乘当前位置的数的个数,同样更新一下数的个数
莫队的我没有注意到的一个trick是
转移区间的时候优先更新右端点,然后左端点,否则可能出现右端点在左端点左边的情况
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
#define LL long long
const int maxn = 31234;
const LL mod = 1000000007;
struct Ask{
int l,r;
int id;
LL ans;
void init(int i){
scanf("%d %d",&l,&r);
id = i;
}
};
Ask ask[maxn];
int unit;
bool cmpmo(Ask a,Ask b){
if(a.l / unit != b.l / unit)
return a.l < b.l;
return a.r < b.r;
}
bool cmpid(Ask a,Ask b){
return a.id < b.id;
}
LL cnt[maxn];
int arr[maxn];
LL rever[maxn];
LL powerMod(LL x,LL n,LL mod){
LL ret = 1;
while(n){
if(n&1) (ret *= x) %= mod;
(x *= x) %= mod;
n >>=1;
}
return ret;
}
LL rev(int x){
if(rever[x] != -1)
return rever[x];
return rever[x] = powerMod(x,mod-2,mod);
}
LL siz;
LL renew(LL ans,int pos,bool adder){
LL & x = cnt[arr[pos]];
if(adder){
x++;
siz++;
(ans *= siz) %= mod;
(ans *= rev(x)) %= mod;
}
else{
(ans *= x) %= mod;
(ans *= rev(siz)) %= mod;
siz--;
x--;
}
return ans;
}
int main(){
int T;
int n,m;
memset(rever,-1,sizeof(rever));
scanf("%d",&T);
while(T-- && ~scanf("%d %d",&n,&m)){
unit = (int)sqrt(n*1.0);
for(int i=1;i<=n;i++){
scanf("%d",&arr[i]);
}
for(int i=0;i<m;i++){
ask[i].init(i);
}
sort(ask,ask+m,cmpmo);
LL ans = 1;
siz = 0;
memset(cnt,0,sizeof(cnt));
for(int i=ask[0].l;i<=ask[0].r;i++){
ans = renew(ans,i,true);
}
ask[0].ans = ans;
int st,ed;
for(int i=1;i<m;i++){
st = min(ask[i].r,ask[i-1].r);
ed = max(ask[i].r,ask[i-1].r);
for(int id=st+1;id<=ed;id++)
ans = renew(ans,id,ed==ask[i].r);
st = min(ask[i].l,ask[i-1].l);
ed = max(ask[i].l,ask[i-1].l);
for(int id=st;id<ed;id++)
ans = renew(ans,id,st==ask[i].l);
ask[i].ans = ans;
}
sort(ask,ask+m,cmpid);
for(int i=0;i<m;i++){
printf("%I64d\n",ask[i].ans);
}
}
return 0;
}