@xiaoziyao
2020-10-13T18:23:24.000000Z
字数 4553
阅读 1157
解题报告
注:集合为质数集。
P4240 毒瘤之神的考验解题报告:
莫比乌斯反演结合根号分治好题。
首先,欧拉函数有一个性质:
证明如下:
由欧拉函数的公式有:
这个式子并不好处理,可以先设,,然后原式化为:
很容易处理,直接枚举因数,然后就可以用总体的复杂度解决。
关于,我们可以得到一个很显然的递推式:。
不妨再设,同样
但是和的大小均可以到达,因此预处理是不可行的,那么预处理更不行了。
考虑根号分治,我们设一个阈值,那么把答案分成两部分考虑:
总复杂度为,取左右可过。
#include<stdio.h>
#include<vector>
#define int long long
using 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;
}