羅区3327:除数と
質問の意味の説明:
- セット\(D(X)\)の\(X \)番号除数、所与\(N、M \)要件:
- \(\ sum_ 1} ^ {N-I = \ sum_ 1} = {J ^ MD)の(IJ \)。\(T \)データのセット。
- データ範囲\(T、N-、M \のLeq 50000 \) 。
思考
- \(D(IJ)= \ sum_ {X | I} \ sum_ {Y | J} [GCD(X、Y)= 1] \)。
- 私たちはあなたを尋ねます:
- \(\ sum_ {i = 1} ^ N \ sum_ {J = 1} ^ M \ sum_ {xは| I} \ sum_ {Y | jは} [GCD(X、Y)= 1] \)。
- 列挙\(X、Y \)です。
- \(\ sum_ {X = 1} ^ N \ sum_ {Y = 1} ^ M [GCD(X、Y)= 1] \ sum_ {iは1 =} ^ N \ sum_ {J = 1} ^ M [X | N \と\ Y | M] \) 。
- \(\ sum_ {X = 1} ^ N \ sum_ {Y = 1} ^ M [GCD(X、Y)= 1] \ FRAC {n}は{X} \ FRAC {M} {Y} \)
- ...変数にそれを変更します。
- \(\ sum_ {I = 1} ^ N \ sum_ {J = 1} ^ M [GCD(I、J)= 1] \ FRAC {n}は{I} \ FRAC {M} {J} \)。
- スタート反転:
- \(F(X)= \ sum_ ^ {I 1 =} N \ sum_ {J = 1} ^ M [GCD(I、J)= X] \ FRAC {n}は{I} \ FRAC {M} {J } \) 。
- ルーチンによると、次のとおりです。
- \(F(X)= \ sum_ {X | D} F(D)= \ sum_ {I = 1} ^ N \ sum_ {J = 1} ^ M \ FRAC {n}は{I} \ FRAC {M} {J} [X | GCD(I、J)] \)。
- 列挙\(IX、JX \)があり、
- \(= \ sum_ {i = 1} ^ {\ FRAC {n}は{X}} \ sum_ {J = 1} ^ {\ FRAC {M} {X}} \ FRAC {N} {IX} \ FRAC { M} {JX} \)。
- それは無視することができるように\(GCD(i、j)は \) 、この条件を。
- そこ反転式:
- \(F(X)= \ sum_ {X | D} \ミュー(\ FRAC {D} {X})F(D)\)。
- 私が知っている(ANS = F(1)\)\。
- \(= \ sum_ {1つの| D} \ MU(D)F(D)\)
- \(= \ sum_ {D = 1} ^ {分(N、M)} \ MU(D)\ sum_ {i = 1} ^ {N / D} \ FRAC {N} {ジ} \ sum_ {J = 1} ^ {M / D} \ FRAC {M} {DJ} \)。
- 実際にあなたはここで行うことができますが、小さな問題があり、それはすぐに計算する方法である(\ sum_ {iは1を=} ^ N- \ N-FRACを{} {I} \)\。
- それは割り切れるブロックで処理することができます。
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef long long ll;
const int maxn = 5e4 + 10;
int T, n, m;
bool vis[maxn];
int primes[maxn], mu[maxn], cnt, sum[maxn];
ll s[maxn];
void get_mu(int n)
{
mu[1] = 1;
for(int i = 2; i <= n; i++)
{
if(!vis[i])
{
primes[++cnt] = i;
mu[i] = -1;
}
for(int j = 1; primes[j] <= n/i; j++)
{
vis[primes[j]*i] = 1;
if(i % primes[j] == 0) break;
else mu[i*primes[j]] = -mu[i];
}
}
for(int i = 1; i <= n; i++)
sum[i] = sum[i-1] + mu[i];
for(int i = 1; i <= n; i++)
{
ll res = 0;
for(int l = 1, r; l <= i; l = r + 1)
{
r = i/(i/l);
res += 1ll*(r-l+1)*1ll*(i/l);
}
s[i] = res;
}
}
inline void solve(int n, int m)
{
if(n > m) swap(n, m);
ll ans = 0;
for(int l = 1, r; l <= n; l = r+1)
{
r = min(n/(n/l), m/(m/l));
ans += 1ll*(sum[r]-sum[l-1])*1ll*s[n/l]*1ll*s[m/l];
}
printf("%lld\n", ans);
}
signed main()
{
get_mu(50000);
scanf("%lld", &T);
while(T--)
{
scanf("%lld%lld", &n, &m);
solve(n, m);
}
return 0;
}