《P2257 YY的GCD》
题目就是求解:$\sum_{i = 1}^{n}\sum_{j = 1}^{m}|gcd(i,j) = prime|$
显然先枚举素数。这里我们定义P为素数集。
即$ans = \sum_{d\epsilon P}^{}\sum_{i = 1}^{n}\sum_{j = 1}^{m}|gcd(i,j) = d|$
变换一下得$ans = \sum_{d\epsilon P}^{}\sum_{i = 1}^{[\frac{n}{d}]}\sum_{j = 1}^{[\frac{m}{d}]}|gcd(i,j) = 1|$
将$|gcd(i,j) = 1|$用莫比乌斯函数替换得:$ans = \sum_{d\epsilon P}^{}\sum_{i = 1}^{[\frac{n}{d}]}\sum_{j = 1}^{[\frac{m}{d}]}\sum_{t | gcd(i,j)}^{}\mu (t)$
改为枚举t得$ans = \sum_{d\epsilon P}^{}\sum_{t = 1}^{min(\frac{n}{d},\frac{m}{d})}[\frac{n}{dt}] * [\frac{m}{dt}] * \mu (t)$
但是到了这里依旧不够快,我们设T = dt。
那么$ans = \sum_{d\epsilon P}^{}\sum_{\frac{T}{d} = 1}^{min(\frac{n}{d},\frac{m}{d})}[\frac{n}{T}] * [\frac{m}{T}] * \mu (\frac{T}{d})$
上下界都乘上d,得$ans = \sum_{d\epsilon P}^{}\sum_{T = 1}^{min(n,m)}[\frac{n}{T}] * [\frac{m}{T}] * \mu (\frac{T}{d})$
然后变形得$ans = \sum_{T = 1}^{min(n,m)}[\frac{n}{T}] * [\frac{m}{T}] * \sum_{d\epsilon P}^{} \mu (\frac{T}{d})$
观察前半部分,显然是一个整除分块。
那么关于后半部分,考虑一下怎么求解。
我们这里一开始枚举了T,因为T = dt,所以T肯定是d的倍数。
即对于每个T,都要加上T除以它的因数的莫比乌斯函数值。
那么,我们一开始都处理出每个T的这个总值即可,然后处理一个前缀和。
这里的前缀和,是因为我们枚举块的时候,块的位置就是T的改变值,那么我们直接可以前缀和O(1)求出块的后半部分值。
这里的分块,对于每个块里得n/T * m/T都是一样的,然后我们乘上了前缀和的后半部分值,所以是不需要乘上块的大小的。(自己好好理解.
还有这里longlong来跑n.m会T,int就能AC,据说int速度是longlong的1 / 4。

#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<double,int> pii; const int N = 1e7+5; const int M = 1e6+5; const LL Mod = 1e9+7; #define rg register #define pi acos(-1) #define INF 1e18 #define CT0 cin.tie(0),cout.tie(0) #define IO ios::sync_with_stdio(false) #define dbg(ax) cout << "now this num is " << ax << endl; namespace FASTIO{ inline LL read(){ LL x = 0,f = 1;char c = getchar(); while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();} return x*f; } void print(int x){ if(x < 0){x = -x;putchar('-');} if(x > 9) print(x/10); putchar(x%10+'0'); } } using namespace FASTIO; int mu[N],prime[N],tot = 0; bool vis[N]; LL f[N],sum[N]; void init() { mu[1] = 1; for(rg int i = 2;i < N;++i) { if(!vis[i]) { prime[++tot] = i; mu[i] = -1; } for(rg int j = 1;j <= tot && i * prime[j] < N;++j) { vis[i * prime[j]] = 1; if(i % prime[j] == 0) break; else mu[i * prime[j]] = -mu[i]; } } for(rg int i = 1;i <= tot;++i) for(rg int j = 1;j * prime[i] < N;++j) f[j * prime[i]] += mu[j]; for(rg int i = 1;i < N;++i) sum[i] = sum[i-1] + f[i]; } LL cal(int n,int m) { LL ans = 0; for(int L = 1,r = 0;L <= min(n,m);L = r + 1) { r = min(n / (n / L),m / (m / L)); ans += 1LL * (n / L) * (m / L) * (sum[r] - sum[L - 1]); } return ans; } int main() { init(); int ca;ca = read(); while(ca--) { LL n,m;n = read(),m = read(); printf("%lld\n",cal(n,m)); } //system("pause"); return 0; }