题解 P9060【[Ynoi2002] Goedel Machine】

$\text{Link}$

JRKSJ 曾经有一道类似的题,但和这题重了。

题意

给一次长为 $n$ 的序列 $a$,每次查询 $[l,r]$ 的所有子序列的 $\gcd$ 的乘积,对 $998244353$ 取模。

$n,q,a_i\le 10^5$。

思路

质因数相关,直接考虑根号分治。由于是乘积,各质因子贡献独立。

记 $v=\max(a_i)$。我们先筛出 $v$ 以内的质数。

对于 $p\le\sqrt v$,这类数只有 $O(\pi(\sqrt v))≈O(\dfrac {\sqrt v}{\log v})$ 个,考虑使用 $O(n\log)$ 级别的做法求出这个质数对所有询问的贡献。考虑记 $s_{i}$ 表示 $\max_{p^k|a_i}k$,记 $e_{i,j}=\sum_{k=1}^i[s_i=j]$,$f_{i,j}=\sum_{k=1}^i[s_i>j]$,则对于询问 $[l,r]$,枚举 $k$ 表示区间内在这个质因数的 $\gcd$ 等于 $p^k$,令 $a=e_{r,k}-e_{l-1,k},b=f_{r,k}-f_{l-1,k}$,则这个询问的答案需要乘上 $p^{k(2^a-1)2^b}$,但是此处的复杂度不允许我们再使用快速幂了。

我们想算出 $p^k$ 恰好会对这个区间贡献多少次,如果指数上是 $2^{a+b}-1$ 则会算多 $p^{k+c}$ 的次数。我们一个麻烦是此处的 $p^k$ 阻碍了我们用 $p^{2^i}$ 计算。但是我们发现如果每个 $c\in[1,k]$ 都贡献一次,那么此处的 $p^k$ 就可以化开了,于是我们可以直接算 $p^{2^{a+b}-1}$ 的积。即 $\prod_k p^{2^{\sum_{i=l}^r[s_i\ge k]}-1}$,$2$ 的指数上的那个可以同样前缀和,$p^{2^k-1}$ 可以预处理 $p^{2^k}$ 和 $p^{-1}$ 求出。

对于 $p>\sqrt n$,我们沿用思路,如果区间中有 $k$ 个含 $p$ 这个质因子的数,则会有 $p^{2^k-1}$ 的贡献。我们使用莫队,预处理所有 $p^{2^k-1}$ 和 $p^{1-2^k}$,由于每个 $p$ 总的出现次数之和是 $n$,我们预处理 $k$ 到 $p$ 的出现次数即可。

时间复杂度为 $O(v+n\pi(\sqrt v)\log v+m\sqrt n)$,空间复杂度 $O(n\log v)$。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
namespace IO{//by cyffff

}
const int N=1e5+10,L=18,mod=998244353;
int n,q,v,a[N],pri[N],cnt,pr[L][N],bgp[N],tot[N],con[N],qp2[N],pt2[N];
bool vis[N];
int curL=1,curR,bel[N],blk,sq=400,T,ans[N],res=1;
vector<int>pqp[N],pip[N];
struct Query{
    int l,r,id;
    inline friend bool operator<(const Query &a,const Query &b){
        return bel[a.l]^bel[b.l]?bel[a.l]<bel[b.l]:(bel[a.l]&1?a.r<b.r:a.r>b.r);
    }
}qr[N];
inline void seive(int n){
    vis[1]=1;
    for(int i=2;i<=n;i++){
        if(!vis[i])
            pri[++cnt]=i;
        for(int j=1;i*pri[j]<=n;j++){
            vis[i*pri[j]]=1;
            if(i%pri[j]==0) break;
        }
    }
}
inline int qpow(int x,int y){
    int res=1;
    while(y){
        if(y&1) res=1ll*res*x%mod;
        x=1ll*x*x%mod;
        y>>=1;
    }
    return res;
}
inline void ins(int p){
    if(p==1) return ;
    res=1ll*res*pip[p][con[p]]%mod;
    con[p]++;
    res=1ll*res*pqp[p][con[p]]%mod;
}
inline void del(int p){
    if(p==1) return ;
    res=1ll*res*pip[p][con[p]]%mod;
    con[p]--;
    res=1ll*res*pqp[p][con[p]]%mod;
}
int main(){
    n=read(),q=read();
    for(int i=1;i<=n;i++)
        v=max(v,a[i]=read());
    seive(v);
    T=sqrt(v)+2;
    for(int i=1;i<=q;i++)
        qr[i].l=read(),qr[i].r=read(),qr[i].id=i,ans[i]=1;
    if(v==1){
        for(int i=1;i<=q;i++)
            write(1),putc('\n');
        flush();
        return 0;
    }
    for(int i=1;i<=n;i+=sq)
        for(int j=i,k=++blk;j<=min(i+sq-1,n);j++)
            bel[j]=k;
    int pos=1;
    for(;pri[pos+1]<=T&&pos+1<=cnt;pos++);
    qp2[0]=1;
    for(int i=1;i<=n;i++)
        qp2[i]=2ll*qp2[i-1]%(mod-1);
    for(int i=1;i<=pos;i++){
        int p=pri[i],ip=qpow(p,mod-2);
        int l=0;
        for(int j=1;j<=v;j*=p,l++);
        l--;
        for(int j=1;j<=n;j++){
            int k=0;
            while(a[j]%p==0) k++,a[j]/=p;
            for(int p=1;p<=l;p++)
                pr[p][j]=pr[p][j-1]+(p<=k);
        }
        pt2[0]=p;
        for(int i=1;i<=n;i++)
            pt2[i]=1ll*pt2[i-1]*pt2[i-1]%mod;
        for(int i=0;i<=n;i++)
            pt2[i]=1ll*pt2[i]*ip%mod;
        for(int j=1;j<=q;j++){
            int L=qr[j].l-1,R=qr[j].r;
            for(int k=1,mul=p;k<=l;k++,mul=mul*p){
                if(mul>v) break;
                int ge=pr[k][R]-pr[k][L];
                ans[j]=1ll*ans[j]*pt2[ge]%mod;
            }
        }
    }
    for(int i=1;i<=n;i++)
        if(a[i]>1)
            tot[a[i]]++;
    for(int i=pos+1;i<=cnt;i++){
        if(!tot[pri[i]]) continue;
        int p=pri[i],ip=qpow(p,mod-2);
        pqp[p].emplace_back(1);
        pip[p].emplace_back(1);
        for(int i=1,tp=1ll*p*p%mod,qp=1ll*ip*ip%mod;i<=tot[p];i++,tp=1ll*tp*tp%mod,qp=1ll*qp*qp%mod){
            pqp[p].emplace_back(1ll*tp*ip%mod);
            pip[p].emplace_back(1ll*qp*p%mod);
        }
    }
    sort(qr+1,qr+q+1);
    for(int i=1;i<=q;i++){
        int l=qr[i].l,r=qr[i].r;
        while(curL>l) ins(a[--curL]);
        while(curR<r) ins(a[++curR]);
        while(curL<l) del(a[curL++]);
        while(curR>r) del(a[curR--]);
        ans[qr[i].id]=1ll*ans[qr[i].id]*res%mod;
    }
    for(int i=1;i<=q;i++)
        write(ans[i]),putc('\n'); 
    flush();
}

再见 qwq~

posted @ 2023-02-12 10:15  ffffyc  阅读(7)  评论(0编辑  收藏  举报  来源