@xiaoziyao
2020-10-13T10:23:24.000000Z
字数 4553
阅读 1685
解题报告
注:集合为质数集。
P4240 毒瘤之神的考验解题报告:
莫比乌斯反演结合根号分治好题。
首先,欧拉函数有一个性质:
证明如下:
由欧拉函数的公式有:
这个式子并不好处理,可以先设,,然后原式化为:
很容易处理,直接枚举因数,然后就可以用总体的复杂度解决。
关于,我们可以得到一个很显然的递推式:。
不妨再设,同样
但是和的大小均可以到达,因此预处理是不可行的,那么预处理更不行了。
考虑根号分治,我们设一个阈值,那么把答案分成两部分考虑:
总复杂度为,取左右可过。
#include<stdio.h>#include<vector>#define int long longusing namespace std;const int maxn=100005,mod=998244353,maxt=55;int T,n,m,cnt,t;int a[maxn],p[maxn],miu[maxn],phi[maxn],nphi[maxn],f[maxn];vector<int>g[maxn],S[maxt][maxt];int ksm(int a,int b){int res=1;while(b){if(b&1)res=res*a%mod;a=a*a%mod,b>>=1;}return res;}void init(){t=50;p[1]=miu[1]=phi[1]=1;for(int i=2;i<maxn;i++){if(p[i]==0)a[++cnt]=i,miu[i]=-1,phi[i]=i-1;for(int j=1;j<=cnt;j++){if(i*a[j]>=maxn)break;p[i*a[j]]=1;if(i%a[j]==0){miu[i*a[j]]=0;phi[i*a[j]]=phi[i]*a[j];break;}miu[i*a[j]]=-miu[i];phi[i*a[j]]=phi[i]*(a[j]-1);}}for(int i=1;i<maxn;i++)nphi[i]=ksm(phi[i],mod-2);for(int i=1;i<maxn;i++)for(int j=1;i*j<maxn;j++)f[i*j]=(f[i*j]+i*nphi[i]%mod*miu[j])%mod;for(int i=1;i<maxn;i++){g[i].push_back(0);for(int j=1;i*j<maxn;j++)g[i].push_back((g[i][j-1]+phi[i*j])%mod);}for(int i=1;i<=t;i++)for(int j=1;j<=t;j++){S[i][j].push_back(0);for(int k=1;k*max(i,j)<maxn;k++)S[i][j].push_back((S[i][j][k-1]+f[k]*g[k][i]%mod*g[k][j]%mod)%mod);}}int solve(int n,int m){int res=0,l,r;for(int i=1;i<=max(n,m)/t;i++)res=(res+f[i]*g[i][n/i]%mod*g[i][m/i]%mod)%mod;l=max(n,m)/t+1;while(l<=min(n,m)){r=min(n/(n/l),m/(m/l));res=(res+(S[n/l][m/l][r]-S[n/l][m/l][l-1]+mod)%mod)%mod;l=r+1;}return res;}signed main(){init();scanf("%lld",&T);while(T--){scanf("%lld%lld",&n,&m);printf("%lld\n",solve(n,m));}return 0;}
