题目大意
扎实的数学功底是每个信息学竞赛选手必须具备的基本素养之一。
比如计算一个正整数m有多少个正约数,通常的做法是先将分解质因数,将每个质因数的幂次加一之后乘起来。例如60=2² * 3 * 5 ,质因数的幂次分别为2 1 1,因此 60 有(2+1)(1+1)(1+1)=12 个正约数。
现在 nodgd 给你一个正整数序列a1,a2…an,并进行很多次询问:序列前k个数的乘积有多少个正约数的所有质因数都不超过p呢?特别的,1没有质因数,所以无论怎么询问1总是符合条件。
第一行两个整数 n , q ,表示序列的长度和询问的次数。
第二行 n 个正整数 a1,a2,a3…an。
从第三行起的连续q行,每行两个正整数 k , p,表示一次询问。
对于每次询问,输出答案后 mod 998244353 的值。
样例数据
input:
5 6
3 4 5 6 7
1 3
2 3
2 2
4 4
4 5
5 7
output:
2
6
3
12
24
48
思路
首先使用欧拉筛选质数法将所有质数打个标记(同时将每个合数是被哪一个质数筛选掉的用数组which存一下),而这个which数组记录的就是每个合数可以整除的最小的质数,之后在分解质因数时会用到。
由于数据很大,所以我们采用树状数组记录每个合数可以分解出的质因数的指数。
之后分解质因数即可,每次将可以分解的最小的质数分解完,若现在可分解的最小的质数变化时,更新树状数组就ok啦!
注意将提问离线。
附代码
#include<stdio.h>
#include<bits/stdc++.h>
#define maxn 100005
#define int long long
const int mod = 998244353;
char buf[1<<20],*p1,*p2;
#define GC (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?0:*p1++)
inline int read(){char t=GC;int x=0;while(!isdigit(t)) t=GC;while(isdigit(t)) x=x*10+t-48,t=GC;return x;}
using namespace std;
struct data{int id,k,p;}question[maxn];
bool cmp(data a,data b){if(a.k==b.k) return a.p<b.p;return a.k<b.k;}
int n,Q,cnt,which[maxn],prime[10005];
bool vis[maxn];
int pow_mod(int a,int i){int res=1;a=a%mod;while(i){if(i&1) res=res*a%mod;a=a*a%mod;i>>=1;}return res;}
void check_prime(){for(int i=2;i<=maxn;i++){if(!vis[i]){prime[++cnt]=i;which[i]=i;}for(int j=1;i*prime[j]<=maxn&&j<=cnt;j++){vis[i*prime[j]]=true;which[i*prime[j]]=prime[j];if(i%prime[j]==0) break;}}}
int inv[maxn],c[maxn];
void get_inv(){inv[1]=1;for(int i=2;i<=maxn;i++){inv[i]=inv[mod%i]*(mod-mod/i)%mod;}for(int i=1;i<=maxn;i++){c[i]=1;}}
int Inv(int a){return pow_mod(a,mod-2);}
void update(int x,int v){for(int i=x;i<=maxn;i+=i&-i){c[i]=c[i]*v%mod;}return;}
int get_ans(int x){int res=1;for(int i=x;i>0;i-=i&-i){res=res*c[i]%mod;}return res;}
int tot[maxn];
void fj(int x){
int num=0,can_fj=which[x];
while(x){
if (can_fj!=which[x]){
int temp=tot[can_fj]+num+1;
if(tot[can_fj]>=maxn) temp=temp*Inv(tot[can_fj]+1)%mod;
else if(tot[can_fj]) temp=temp*inv[tot[can_fj]+1]%mod;
update(can_fj,temp);
tot[can_fj]+=num;
num=0;
can_fj=which[x];
}
if(x==1) return;
num++,x/=which[x];
}
return;
}
int a[maxn],Ans[maxn];
main(){
n=read(),Q=read();
check_prime();
get_inv();
for(int i=1;i<=n;i++){a[i]=read();}
for(int i=1;i<=Q;i++){question[i].k=read(),question[i].p=read(),question[i].id=i;}
sort(question+1,question+1+Q,cmp);
int i=1,j=0;
while(i<=Q){
while(j<question[i].k){fj(a[++j]);}
while(j==question[i].k){Ans[question[i].id]=get_ans(question[i].p),i++;}
}
for(int i=1;i<=Q;i++){printf("%lld\n",Ans[i]);}
return 0;
//By--oopst
}