P4240 毒瘤之神的考验

P4240 毒瘤之神的考验

链接

P4240 毒瘤之神的考验

题解

假定\(n \leq m\) ,(交换n,m显然不影响答案)
首先欧拉函数有个性质。。 \(\phi(xy)\) \(=\) \(\frac{\phi(x) \phi(y) (x,y)}{\phi((x,y))}\)
代入题目 \(\sum_{x=1}^{n}\sum_{y=1}^{m}\frac{\phi(x) \phi(y) (x,y)}{\phi((x,y))}\)
然后是喜闻乐见的枚举公因数,在转化为莫比乌斯函数。。。
\(\sum_{d=1}^{n}\sum_{t=1}^{\lfloor \frac{n}{d} \rfloor} \frac{d\mu(t)}{\phi(d)} \sum_{x=1}^{\lfloor \frac{n}{dt}\rfloor} \phi(xtd) \sum_{y=1}^{\lfloor \frac{m}{dt}\rfloor}\phi(ytd)\)
我们设:
\(A(p)\) \(=\) \(\sum_{d|p}\frac{d}{\phi(d)} \mu(\frac{p}{d})\)
\(D(x,p)\) \(=\) \(\sum_{i=1}^{x}\phi(ip)\)
于是答案可以表示为 \(\sum_{p=1}^{n}A(p)D(\lfloor \frac{n}{p} \rfloor,p)D(\lfloor \frac{m}{p} \rfloor,p)\)
\(A(p),D(x,p)\)两种函数都是可以\(O(nlogn)\)预处理出来的,于是此时我们已经得到\(O(nlogn+qn)\)的做法了
我们需要进一步优化单次询问的效率。
\(S(lim,x,y)\) \(=\) \(\sum_{p=1}^{lim}A(p)D(x,p)D(y,p)\)
如果对于任意\(S(lim,x,y)\)都能\(O(1)\)询问的话,就可以套用整除分块了。。。然而这并不容易做到。
我们可以先预处理一些\(S(lim,x,y)\),这样对于部分p值就能快速询问了。
假设我们预处理出所有满足\(x,y \leq B\)的数据,那么对于 \(\lfloor \frac{m}{p} \rfloor \leq B\) 的都可以\(O(1)\)询问,这部分效率是\(O(n^{0.5})\)
对于\(\lfloor \frac{m}{p} \rfloor > B\) 的情况,这样的p是满足\(p<\lfloor \frac{m}{B} \rfloor\),这部分直接算,不用整除分块,效率是\(O(\lfloor \frac{m}{B} \rfloor)\)
于是总效率就是\(O(nlogn+q(n^{0.5}+\lfloor \frac{m}{B} \rfloor))\)

\(Code\)

#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N=1e5+10;
const LL P=998244353;
const int E=90;
int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
void print(LL x){
    if(x>9) print(x/10);
    putchar(x%10+'0');
}
LL qpow(LL x,LL y,LL p){
    LL re=1;
    while(y){
        if(y&1) re=re*x%p;
        x=x*x%p;y>>=1;
    }
    return re;
}
int n,m;
LL phi[N],mu[N];
LL ni_phi[N];
LL A[N];
vector<LL> D[N],S[100][100];
bool is_pri[N+10];
int pri[N],cntp=0;
void init_pri(){
    mu[1]=1;phi[1]=1;
    for(int i=2;i<=100000;++i){
        if(!is_pri[i]) {
            pri[++cntp]=i;
            mu[i]=P-1;
            phi[i]=i-1;
        }
        for(int j=1;j<=cntp&&pri[j]*i<=100000;++j){
            is_pri[pri[j]*i]=1;
            if(i%pri[j]==0) {
                phi[pri[j]*i]=phi[i]*pri[j];
                mu[pri[j]*i]=0;
                break;
            }
            else{
                phi[pri[j]*i]=phi[i]*(pri[j]-1);
                mu[pri[j]*i]=P-mu[i];
            }
        }
    }
}
int main(){
    init_pri();
    for(int i=1;i<=100000;++i) ni_phi[i]=qpow(phi[i],P-2,P);
    LL x,y;
    for(int i=1;i<=100000;++i){
        x=(LL)i*ni_phi[i]%P;
        for(int j=1;j<=100000/i;++j){
            A[i*j]+=x*mu[j]%P;
        }
        A[i]=A[i]%P;
    }
    for(int p=1;p<=100000;++p){
        x=0;D[p].push_back(x);
        for(int i=1;i<=100000/p;++i){
            x+=phi[i*p];
            if(x>=P)x-=P;
            D[p].push_back(x);
        }
    }
    for(int i=1;i<=E;++i){
        for(int j=1;j<=i;++j){
            x=0;S[i][j].push_back(x);
            for(int p=1;p<=100000/i;++p){
                x+=A[p]*D[p][i]%P*D[p][j]%P;
                if(x>=P)x-=P;
                S[i][j].push_back(x);
            }
        }
    }
    int T,k,l;scanf("%d",&T);
    LL ans;
    while(T--){
        scanf("%d%d",&n,&m);ans=0;
        if(n>m) swap(n,m);
        for(int i=1,j;i<=n;i=j+1){
            j=i;
            l=n/i;k=m/i;
            if(k>E){
                ans+=A[i]*D[i][l]%P*D[i][k]%P;
            }
            else{
                j=min(n/l,m/k);
                ans+=(S[k][l][j]-S[k][l][i-1]+P)%P;
            }
        }
        ans=(ans%P+P)%P;
        printf("%lld\n",ans);
    }
    return 0;
}
posted @ 2020-12-25 22:50  Iscream-2001  阅读(61)  评论(0编辑  收藏  举报
/* */