1. 程式人生 > 實用技巧 >P4240 毒瘤之神的考驗

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;
}