Solution P12524 [Aboi Round 1] I なんです
首先 gcd 的平方是骗人的,因为求的是积,平方最后乘上即可。
有一个 gcd 的转化:
\[\gcd(S)=\prod_{p\in\mathbb{P}}\prod_{k\ge 1,\forall x\in S,p_i^k|x}p_i
\]
验证一下可以发现这是对的。
考虑对每个质数 \(p\) 统计答案。考虑枚举 \(p,k\),判断区间 \([l,r]\) 内有多少个数是 \(p^k\) 倍数(记为 \(c\)),根据组合数知识可得 \(c\) 中选奇数或偶数个的方案数是相等的。因此可行的 \(S\) 共 \(2^{c-1}\) 个(\(c=0\) 时特判一下)。
但若对所有质数都扫一遍整个数组枚举答案不太牛。考虑经典 trick:记 \(V\) 表示值域上界,显然所有数都可以表示成若干个小于 \(\sqrt V\) 的质数和最多一个大于 \(\sqrt V\) 的质数的乘积。因此考虑根号分治。
大质数出现次数总和最多为 \(n\),且对应的 \(k\) 一定为 \(1\),这个随便拿个莫队维护区间内的 \(p^{(2^{cnt_p-1})}\) 就可以了。如果直接做可能是 \(O(n\sqrt q\log V)\) 的,因为要快速幂。可以套个离散对数板子做到 \(O(n\sqrt q)\)。
小质数共有 \(O(\frac{\sqrt V}{\log V})\) 个。枚举每个小质数,然后 \(O(\log V)\) 枚举 \(k\),先扫一遍数组做前缀和,然后每个询问就可以 \(O(1)\) 查了。\(\log V\) 消掉了,总的时间复杂度是 \(O(q\sqrt V)\) 的。
设 \(n,q,V\) 同阶,时间复杂度 \(O(n\sqrt n)\)。
const int N=100005,B=320,P=998244353;
vector<int>pr;
bool np[N];
int n,q,a[N],mxp[N],pw2[N],lg[N];
int id[N],cnt[N],ans[N],res=0;
il void add(int x){
x=mxp[x];if(!x)return;
if(cnt[x])res=(res-1ll*lg[x]*pw2[cnt[x]-1]%(P-1)+P-1)%(P-1);
cnt[x]++;
res=(res+1ll*lg[x]*pw2[cnt[x]-1])%(P-1);
}
il void del(int x){
x=mxp[x];if(!x)return;
res=(res-1ll*lg[x]*pw2[cnt[x]-1]%(P-1)+P-1)%(P-1);
cnt[x]--;
if(cnt[x])res=(res+1ll*lg[x]*pw2[cnt[x]-1])%(P-1);
}
struct Q{
int l,r,i;
il friend bool operator<(Q &x,Q &y){
if(id[x.l]!=id[y.l])return x.l<y.l;
else return (id[x.l]&1)?(x.r<y.r):(x.r>y.r);
}
}qr[N];
signed main(){
BSGS::init(3,P);
forto(i,2,B){
if(!np[i])pr.eb(i);
for(int j:pr){
if(i*j>B)break;
np[i*j]=1;
if(i%j==0)break;
}
}
n=read(),q=read();
pw2[0]=1;forto(i,1,n)pw2[i]=pw2[i-1]*2%(P-1);
int x,mx=0;
forto(i,1,n){
x=a[i]=read(),id[i]=i/B,mx=max(mx,a[i]);
for(int j:pr)while(x%j==0)x/=j;
if(x>1){
if(!lg[x])lg[x]=BSGS::qry(x);
mxp[i]=x;
}
}
forto(i,1,q)qr[i].l=read(),qr[i].r=read(),qr[i].i=i;
sort(qr+1,qr+q+1);
int l=1,r=0;
forto(i,1,q){
while(l>qr[i].l)add(--l);
while(r<qr[i].r)add(++r);
while(l<qr[i].l)del(l++);
while(r>qr[i].r)del(r--);
ans[qr[i].i]=res;
}
int k;
for(int p:pr){
k=BSGS::qry(p);
for(int pk=p;;pk*=p){
cnt[0]=0;
forto(i,1,n)cnt[i]=cnt[i-1]+(a[i]%pk==0);
forto(i,1,q){
x=cnt[qr[i].r]-cnt[qr[i].l-1];
if(x)ans[qr[i].i]=(ans[qr[i].i]+1ll*k*pw2[x-1])%(P-1);
}
if(1ll*pk*p>mx)break;
}
}
forto(i,1,q)printf("%lld\n",fpow(3,ans[i]*2%(P-1),P));
return 0;
}