[luogu 4240] 毒瘤之神的考驗
題目背景
Salamander的家門口是一條長長的公路。
又是一年春天將至,Salamander發現路邊長出了一排毒瘤!
Salamander想帶一些毒瘤回家,但是,這時毒瘤當中鑽出來了一個毒瘤之神!
毒瘤之神:你想要帶毒瘤走嗎?想要帶走毒瘤,就必須回答我的問題!如果答不出來的話,你還是乖乖回家吧!
題目描述
毒瘤之神會問T次,每次給定n,m,Salamander需要回答出\(\sum_{i=1}^n\sum_{j=1}^m\varphi(ij)\ mod\ 998244353\)。
Salamander這麼辣雞當然不會做啦,於是把問題丟給了你。
輸入輸出格式
輸入格式:
第一行包含一個正整數T。
接下來T行,每行包含兩個正整數,用空格隔開,表示這次詢問的n,m。
輸出格式:
包含T行每行一個整數表示答案。
輸入輸出樣例
輸入樣例#1:
3
1 1
2 2
3 3
輸出樣例#1:
1
5
19
Solution
神奇的反演題。。
首先有一個比較顯然的性質:
\[ \varphi(ij)=\frac{\varphi(i)\varphi(j)\gcd(i,j)}{\varphi(\gcd(i,j))} \]
證明對於每個質因子考慮就行了。
然後帶進去莫比烏斯反演:
\[ \begin{align} ans=&\sum_{i=1}^{n}\sum_{j=1}^m\frac{\varphi(i)\varphi(j)\gcd(i,j)}{\varphi(\gcd(i,j))}\\ =&\sum_{T=1}^{\min(n,m)}\sum_{d|T}\frac{d}{\varphi(d)}\cdot \mu(\frac{T}{d}) \sum_{i=1}^{\lfloor\frac{n}{T}\rfloor}\varphi(iT)\sum_{j=1}^{\lfloor\frac{m}{T}\rfloor}\varphi(jT) \end{align} \]
設:
\[ f(n)=\sum_{d|n}\frac{d}{\varphi(d)}\cdot \mu(\frac{n}{d}) \]
顯然這玩意可以\(O(n\log n)\)預處理出來。
對於後面那個,設:
\[ g(n,T)=\sum_{i=1}^{n}\varphi(iT) \]
然後因為\(n\cdot T\leqslant 1e5\),所以總共只有\(O(n\log n)\)個值,動態開記憶體預處理出來就行。
答案變成了:
\[ ans=\sum_{t=1}^{\min(n,m)}f(t)g(\lfloor\frac{n}{t}\rfloor,t)g(\lfloor\frac{m}{t}\rfloor,t) \]
答案是一個數論分塊的形式,設:
\[ h(n,m,t)=\sum_{i=1}^{t}f(i)g(n,i)g(m,i) \]
然後我們預處理出\(n,m\leqslant B\)的值,這裡先設一個\(B\)出來,現在並不知道他是多少。
然後分析下複雜度,此時\(ans\)裡的\(T\geqslant n/B\),所以對於\(T \leqslant n/B\)的直接暴力來算出就好了,剩下的數論分塊之後利用\(h\)減一下就好了。
時間複雜度為\(O(n\log n+B^2n+T(\sqrt{n}+\lfloor\frac{n}{B}\rfloor))\)。
取最小值即\(B^2n=T\cdot \frac{n}{B}\),解得\(B=\sqrt[3]{T}\thickapprox 30\)。
#include<bits/stdc++.h>
using namespace std;
void read(int &x) {
x=0;int f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}
void print(int x) {
if(x<0) putchar('-'),x=-x;
if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}
#define pb push_back
const int maxn = 1e5+10;
const int B = 30;
const int mod = 998244353;
int pri[maxn],mu[maxn],vis[maxn],tot,f[maxn],phi[maxn],inv[maxn];
vector <int > g[maxn],h[B+1][B+1];
void prepare(int n) {
mu[1]=inv[1]=phi[1]=1;
for(int i=2;i<=n;i++) {
if(!vis[i]) mu[i]=-1,pri[++tot]=i,phi[i]=i-1;
for(int j=1;j<=tot&&i*pri[j]<=n;j++) {
vis[i*pri[j]]=1;
if(i%pri[j]==0) {phi[i*pri[j]]=phi[i]*pri[j];break;}
mu[i*pri[j]]=-mu[i];
phi[i*pri[j]]=phi[i]*(pri[j]-1);
}
inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
}
for(int d=1;d<=n;d++)
for(int T=d;T<=n;T+=d)
f[T]=(f[T]+1ll*d*inv[phi[d]]%mod*mu[T/d])%mod;
for(int i=0;i<=n;i++) g[0].pb(0);
for(int i=1;i<=n;i++) {
int l=(n/i);g[i].pb(0);
for(int j=1;j<=l;j++)
g[i].pb((g[i-1][j]+phi[i*j])%mod);
}
for(int i=1;i<=B;i++)
for(int j=1;j<=B;j++) {
int l=min(n/i,n/j);h[i][j].pb(0);
for(int k=1;k<=l;k++)
h[i][j].pb((h[i][j][k-1]+1ll*f[k]*g[i][k]%mod*g[j][k]%mod)%mod);
}
}
void solve() {
int n,m;read(n),read(m);if(n>m) swap(n,m);
int ans=0;
for(int i=1;i<=m/B;i++) ans=(ans+1ll*f[i]*g[n/i][i]%mod*g[m/i][i]%mod)%mod;
int T=m/B+1;
while(T<=n) {
int pre=T;T=min(n/(n/T),m/(m/T));
ans=(1ll*(ans+h[n/T][m/T][T])-h[n/T][m/T][pre-1])%mod;T++;
}write((ans+mod)%mod);
}
int main() {
prepare(100000);
int t;read(t);
while(t--) solve();
return 0;
}