题目描述
珂朵莉给你一个长为n的序列,有m次查询
每次查询给两个数l,r
设s为区间[l,r]内所有数的乘积
求s的约数个数mod 1000000007
输入描述:
第一行两个正整数n,m
第二行一个长为n的序列
之后m行每行两个数l和r
输出描述:
对于每个询问,输出一个整数表示答案
示例1
输入
复制
5 5
64 2 18 9 100
1 5
2 4
2 3
1 4
3 4
输出
复制
165
15
9
45
10
备注:
对于100%的数据,有n , m <= 100000 , a[i] <= 1000000
题解:
莫队+数论(约数个数)
很容易看出是莫队的题,个人认为难点在于求约数的个数,常规的求约束的个数肯定不行,有一个叫约束个数定理的东西
任何一个大于1的n都可以分解:
n=p1a1×p2a2×p3a3*…*pkak,p为素数
而n的约数的个数就是(a1+1)(a2+1)(a3+1)…(ak+1)
再看一下本题数据,对于不超过1000的素数我们可以直接维护每个素数的幂指数+1的前缀乘积(共168个),而超过1000的素因数最多也就一个。代码中ant用来记录1000之外的素因子,res用来记录1000之内的每个素数的幂指数+ 1 +1+1的前缀乘积
线性筛就是每一次被最小素因数给筛出来,所以用线性筛来计算因数和。因为题目要mod,所以我们还要线性预处理逆元,方便后面使用
nv[0] = inv[1] = 1; for(int i=2;i<=n+1;i++) inv[i]=1ll*(MOD-MOD/i)*inv[MOD%i]%MOD ;
具体线性筛如何求因数和可以看其他博客讲解
具体代码如下
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
const int MOD = 1e9+7;
int prime[1007],tot;
bool vis[1007];
ll inv[N],ans[N],ant;
int Be[N],a[N],sum[N*10],pre[N][170]; // sum存 在区间内 > 1000 的素数的个数
void init()
{
tot = 0;
for(int i = 2;i <= 1000;i++)
{
if(vis[i]) continue;
prime[tot++] = i;
for(int j = i + i; j <= 1000; j += i )
vis[j] = 1;
}
}
struct Mo{
int l,r,id;
}Q[N];
int cmp(Mo a,Mo b){
return Be[a.l] == Be[b.l] ? a.r < b.r : a.l < b.l;}
void add(int pos)
{
if(a[pos] == 1) return;
ant = ant*inv[1+sum[a[pos]]]%MOD;
sum[a[pos]]++;
ant = ant*(1+sum[a[pos]])%MOD;
}
void del(int pos)
{
if(a[pos] == 1) return;
ant = ant*inv[1+sum[a[pos]]]%MOD;
sum[a[pos]]--;
ant = ant*(1+sum[a[pos]])%MOD;
}
int main()
{
int n,m;
init();
scanf("%d%d",&n,&m);
inv[0] = inv[1] = 1; for(int i=2;i<=n+1;i++) inv[i]=1ll*(MOD-MOD/i)*inv[MOD%i]%MOD ; // 逆元筛
int len = sqrt(n);
for(int i = 1;i <= n;i++)
{
scanf("%d",&a[i]);
Be[i] = i/len;
for(int j = 0;j < tot;j++)
{
pre[i][j] = pre[i-1][j];
while(a[i] % prime[j] == 0)//如果这个质数是因子
{
pre[i][j]++;
a[i] /= prime[j];
}
}
}
for(int i = 1;i <= m;i++) scanf("%d%d",&Q[i].l,&Q[i].r),Q[i].id = i;
sort(Q+1,Q+m+1,cmp);
memset(sum,0,sizeof(sum));
ant = 1;
int l = 1,r = 0;
for(int i = 1;i <= m;i++)
{
while(r < Q[i].r) add(r+1),r++;
while(r > Q[i].r) del(r),r--;
while(l < Q[i].l) del(l),l++;
while(l > Q[i].l) add(l-1),l--;
ll res = 1;
for(int j=0;j<tot;j++)
res=1ll*res*(pre[r][j]-pre[l-1][j]+1)%MOD;
ans[Q[i].id] = 1ll*ant*res%MOD;
}
for(int i = 1;i <= m;i++)
printf("%lld\n",ans[i]);
return 0;
}