版权声明:转载注明出处,谢谢,有问题可以向博主联系 https://blog.csdn.net/VictoryCzt/article/details/85043474
题目地址
题意简述
有
T次询问,每次给定
n,m,求下面式子的值:
i=1∑nj=1∑mφ(ij)
n,m≤105,输出答案在
mod998244353意义下的值。
分析:
我们先要将
φ(ij)拆开,根据定义式:
φ(n)=np∣n∏pp−1其中p为n的不同的质数因子
那么我们看
φ(i)φ(j)就为:
ip∣i∏pp−1⋅jq∣j∏qq−1
我们将
ij放在一起可以发现:
ijp∣i∏pp−1q∣j∏qq−1
而
i,j的质因子可以看成属于
i×j的所有质因子,而既属于
i又属于
j的,也就是属于
gcd(i,j)的质因子会被算两次,所以,原式可以转化为:
ijp∣ij∏pp−1q∣gcd(i,j)∏qq−1
那么,我们将原等式两端同时乘以一个
gcd(i,j)可以得到:
φ(i)φ(j)gcd(i,j)=ijp∣ij∏pp−1gcd(i,j)q∣gcd(i,j)∏qq−1
我们将其替换为
φ(ij)和
φ(gcd(i,j)),就可以得到:
φ(i)φ(j)gcd(i,j)=φ(ij)φ(gcd(i,j))φ(ij)=φ(gcd(i,j))φ(i)φ(j)gcd(i,j)
所以我们可以将原式拆开,转化成求:
i=1∑nj=1∑mφ(gcd(i,j))φ(i)φ(j)gcd(i,j)
根据反演题目的套路,我们转换为枚举
gcd(i,j),原式就变为(这里默认
n≤m如果不满足就交换):
=d=1∑ni=1∑nj=1∑mφ(d)φ(i)φ(j)d[gcd(i,j)=d]=d=1∑nφ(d)di=1∑nj=1∑mφ(i)φ(j)[gcd(i,j)=d]=d=1∑nφ(d)di=1∑⌊dn⌋i=1∑⌊dm⌋φ(id)φ(jd)[gcd(i,j)=1]
然后就是经典的莫比乌斯反演的套路了,我们将后面的两个求和反演后得到(其中
w∣gcd(i,j)=w∣i,w∣j):
i=1∑⌊dn⌋i=1∑⌊dm⌋φ(id)φ(jd)[gcd(i,j)=1]=i=1∑⌊dn⌋i=1∑⌊dm⌋φ(id)φ(jd)w∣i,w∣j∑μ(w)=w∣d∑μ(wd)i=1∑⌊dn⌋i=1∑⌊dm⌋φ(id)φ(jd)
然后我们将原式更换枚举顺序,可以得到:
=d=1∑nw∣d∑φ(w)wμ(wd)i=1∑⌊dn⌋φ(id)j=1∑⌊dm⌋φ(jd)
然后我们令
f(n)=∑d∣nφ(d)dμ(dn),
g(n,d)=∑i=1nφ(id),原式就可以写成:
=d=1∑nf(d)g(⌊dn⌋,d)g(⌊dm⌋,d)
那么我们可以先线性筛出
φ(i),μ(i),然后枚举因子和倍数(类似诶氏筛的方式)
O(nlogn)的筛出
f(i),g(i,j),其中
g(i,j)=g(i−1,j)+φ(ij)且
i≤jn,但是每次回答还是要
O(n),所以我们这里采用分块打表的技巧,我们令答案
T(n,x,y)=∑i=1nf(i)g(x,i)g(y,i),我们预处理处
x,y≤100左右的答案,复杂度为
O(2100×101×∑i=1nin),如果觉的常数大就可以少预处理一点。然后每次回答的时候,对于
i,j小于
100m的部分我们直接暴力算,而大于的部分我们已经预处理出来了,直接分块算,每次询问的复杂度为
O(m
+100m)就可以过了。
空间上不要直接开满,用不定长数组
vector可以节省大量空间。
常数炒鸡大和炒鸡丑陋的代码:
#include<vector>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int M=1e5+10,N=101,W=1e5;
const int Mod=998244353;
ll prime[M],phi[M],mu[M],cnt;
ll F[M],inv[M];
vector <ll> T[N][N],G[M];
bool vis[M];
void init(){
inv[0]=inv[1]=1;
mu[1]=phi[1]=1;
for(ll i=2;i<=W;i++){
inv[i]=(Mod-Mod/i)*inv[Mod%i]%Mod;
if(!vis[i]){
prime[++cnt]=i;mu[i]=Mod-1;
phi[i]=i-1;
}
for(ll j=1,v;j<=cnt&&i*prime[j]<=W;j++){
v=i*prime[j];
vis[v]=1;
if(!(i%prime[j])){
phi[v]=phi[i]*prime[j];
break;
}
mu[v]=(Mod-mu[i])%Mod;
phi[v]=phi[i]*phi[prime[j]];
}
}
for(ll i=1,v;i<=W;i++){
v=inv[phi[i]]*i%Mod;
for(int j=1;i*j<=W;++j){
F[i*j]=(F[i*j]+v*mu[j]%Mod)%Mod;
}
}
for(ll i=1;i<=W;i++){
int L=W/i+1;G[i].push_back(0);
for(ll j=1;j<L;j++){
G[i].push_back((G[i][j-1]+phi[i*j])%Mod);
}
}
for(int x=1;x<N;x++){
for(int y=x;y<N;y++){
int L=W/y+1;
T[x][y].push_back(0);
for(int i=1;i<L;i++){
T[x][y].push_back((T[x][y][i-1]+F[i]*G[i][x]%Mod*G[i][y]%Mod)%Mod);
}
}
}
}
int n,m;
ll ans;
void solve(){
scanf("%d%d",&n,&m);
ans=0;
if(n>m)swap(n,m);
int B=m/N;if(B>n)B=n;
for(int i=1;i<=B;i++){
ans=(ans+F[i]*G[i][n/i]%Mod*G[i][m/i]%Mod)%Mod;
}
for(int i=B+1,j;i<=n;i=j+1){
j=min(n/(n/i),m/(m/i));
ans=((ans+T[n/i][m/i][j])%Mod+Mod-T[n/i][m/i][i-1])%Mod;
}
printf("%lld\n",ans);
}
int Test;
int main(){
init();
for(scanf("%d",&Test);Test--;)solve();
return 0;
}