1. 程式人生 > 實用技巧 >題解 毒瘤之神的考驗

題解 毒瘤之神的考驗

題目傳送門

題目大意

\(T\)組查詢,每次查詢給出\(n,m\),求出:

\[\sum_{i=1}^{n} \sum_{j=1}^{m} \varphi(ij) \bmod 998244353 \]

\(T\le 10^4,n,m\le 10^5\)

思路

首先,你需要知道一個東西:

\[\varphi(ij)=\dfrac{\varphi(i)\varphi(j)\gcd(i,j)}{\varphi(\gcd(i,j))} \]

證明的話直接展開即可。

於是,我們就可以推得答案即為:

\[\sum_{k=1}^{\min(n,m)}(\sum_{d|k} \dfrac{d}{\varphi(d)}\mu(\dfrac{k}{d}))(\sum_{i=1}^{\lfloor\frac{n}{k}\rfloor} \varphi(ik))(\sum_{j=1}^{\lfloor\frac{m}{k}\rfloor}\varphi(jk)) \]

我們發現前面那個東西我們可以\(\Theta(n\log n)\)預處理出來,我們設為\(F(k)\),那麼\(F(k)=\sum_{d|k} \dfrac{d}{\varphi(d)}\mu(\frac{k}{d})\),所以式子就是:

\[\sum_{k=1}^{\min(n,m)} F(k) (\sum_{i=1}^{\lfloor\frac{n}{k}\rfloor} \varphi(ik))(\sum_{j=1}^{\lfloor\frac{m}{k}\rfloor}\varphi(jk)) \]

我們發現如果我們設\(G(y,x)=\sum_{i=1}^{x} \varphi(iy)\)

,那麼答案即為:

\[\sum_{k=1}^{\min(n,m)}F(k) G(k,\lfloor\frac{n}{k}\rfloor)G(k,\lfloor\frac{m}{k}\rfloor) \]

所以,如果我們設答案\(S(y,z,x)=\sum_{k=1}^{x} F(k)(\sum_{i=1}^{y} \varphi(ik))(\sum_{j=1}^{z} \varphi(jk))\)

那麼可以得到:

\[S(y,z,x)=S(y,z,x-1)+F(x)G(x,y)G(x,z) \]

然後我們結合一下式子,就可以發現答案是可以整除分塊的。但是直接這樣搞的話預處理是\(\Theta(n^2)\)

的,於是我們可以設一個閾值,然後卡一下就可以過了。

\(\texttt{Code}\)

#include <bits/stdc++.h>
using namespace std;

#define Int register int
#define mod 998244353
#define MAXN 100005
#define B 35

int *G[MAXN],*S[B + 5][B + 5];
int T,cnt,F[MAXN],mu[MAXN],inv[MAXN],phi[MAXN],vis[MAXN],prime[MAXN];

int mul (int a,int b){return 1ll * a * b % mod;}
int dec (int a,int b){return a >= b ? a - b : a + mod - b;}
int add (int a,int b){return a + b >= mod ? a + b - mod : a + b;}

void Euler (int up){
	mu[1] = phi[1] = inv[1] = 1;
	for (Int i = 2;i <= up;++ i){
		if (!vis[i]) prime[++ cnt] = i,phi[i] = i - 1,mu[i] = -1;
		for (Int j = 1;j <= cnt && i * prime[j] <= up;++ j){
			vis[i * prime[j]] = 1;
			if (i % prime[j] == 0){
				phi[i * prime[j]] = phi[i] * prime[j];
				mu[i * prime[j]] = 0;
				break;
			}
			else mu[i * prime[j]] = -mu[i],phi[i * prime[j]] = phi[i] * (prime[j] - 1);
		}
	}
	for (Int i = 2;i <= up;++ i) inv[i] = mod - 1ll * (mod / i) * inv[mod % i] % mod;
	for (Int i = 1;i <= up;++ i) mu[i] = mu[i] < 0 ? mod + mu[i] : mu[i];
	for (Int i = 1;i <= up;++ i)
		for (Int j = 1;i * j <= up;++ j)
			F[i * j] = add (F[i * j],mul (mul (i,inv[phi[i]]),mu[j]));
	for (Int i = 1;i <= up;++ i){
		G[i] = new int [up / i + 1];G[i][0] = 0;
		for (Int j = 1;j <= up / i;++ j)
			G[i][j] = add (G[i][j - 1],phi[i * j]);
	}
	for (Int j = 1;j <= B;++ j)
		for (Int k = 1;k <= B;++ k){
			int len = up / max (j,k);
			S[j][k] = new int [len + 1];S[j][k][0] = 0;
			for (Int i = 1;i <= len;++ i)
				S[j][k][i] = add (S[j][k][i - 1],mul (F[i],mul (G[i][j],G[i][k])));
		}
}

int Solve (int n,int m){
	if (n > m) swap (n,m);int sum = 0;
	for (Int i = 1;i <= m / B;++ i) sum = add (sum,mul (F[i],mul (G[i][n / i],G[i][m / i])));
	for (Int l = m / B + 1,r;l <= n;l = r + 1){
		r = min (n / (n / l),m / (m / l));
		sum = add (sum,dec (S[n / l][m / l][r],S[n / l][m / l][l - 1]));
	}
	return sum;
}

template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}

signed main(){
	read (T),Euler (1e5);
	while (T --){
		int n,m;read (n,m);
		write (Solve (n,m)),putchar ('\n');
	}
	return 0;
}