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