前置知识:
1.数论分块,二维数论分块
2. ⌊ a b c ⌋ = ⌊ ⌊ a b ⌋ c ⌋ \lfloor\frac{a}{bc}\rfloor = \lfloor\frac{\lfloor\frac{a}{b}\rfloor}{c}\rfloor ⌊bca⌋=⌊c⌊ba⌋⌋
3. g c d ( i , j ) = k → g c d ( i k , j k ) = 1 gcd(i,j)=k \to gcd(\frac ik,\frac jk)=1 gcd(i,j)=k→gcd(ki,kj)=1
OI WIKI 上面莫比乌斯反演前置知识
题目大意:
求 ∑ i = a b ∑ j = c d [ g c d ( i , j ) = = k ] \sum_{i=a}^{b}\sum_{j=c}^{d}[gcd(i,j)==k] ∑i=ab∑j=cd[gcd(i,j)==k]
所 有 变 量 ≤ 5 e 4 所有变量 \leq 5e4 所有变量≤5e4
题目思路:
莫比乌斯反演主要就是公式推导。
这个二维区间函数和容斥一下变成求 ∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = = k ] \sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i,j)==k] ∑i=1n∑j=1m[gcd(i,j)==k]
原式 = ∑ i = 1 n ∑ j = 1 m [ g c d ( i k , j k ) = = 1 ] =\sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(\frac ik,\frac jk)==1] =∑i=1n∑j=1m[gcd(ki,kj)==1]
= ∑ i = 1 ⌊ n k ⌋ ∑ j = 1 ⌊ m k ⌋ [ g c d ( i , j ) = = 1 ] =\sum_{i=1}^{\lfloor \frac nk\rfloor}\sum_{j=1}^{\lfloor \frac mk\rfloor}[gcd(i,j)==1] =∑i=1⌊kn⌋∑j=1⌊km⌋[gcd(i,j)==1]
= ∑ i = 1 ⌊ n k ⌋ ∑ j = 1 ⌊ m k ⌋ ∑ d ∣ g c d ( i , j ) μ ( d ) =\sum_{i=1}^{\lfloor \frac nk\rfloor}\sum_{j=1}^{\lfloor \frac mk\rfloor}\sum_{d|gcd(i,j)}^{}\mu( d) =∑i=1⌊kn⌋∑j=1⌊km⌋∑d∣gcd(i,j)μ(d)
接下来变换求和顺序。按约数来计算 μ \mu μ的和
= ∑ d = 1 m i n ( ⌊ n k ⌋ , ⌊ m k ⌋ ) μ ( d ) ∑ d ∣ i ⌊ n k ⌋ ∑ d ∣ j ⌊ m k ⌋ =\sum _{d=1} ^{min(\lfloor \frac nk\rfloor, \lfloor \frac mk\rfloor)} \mu(d) \sum _{d|i} ^{\lfloor \frac nk\rfloor} \sum _{d|j} ^{\lfloor \frac mk\rfloor} =∑d=1min(⌊kn⌋,⌊km⌋)μ(d)∑d∣i⌊kn⌋∑d∣j⌊km⌋
显然, [ 1 , ⌊ n k ⌋ ] [1,\lfloor \frac{n}{k} \rfloor] [1,⌊kn⌋]内有 ⌊ ⌊ n k ⌋ d ⌋ \lfloor\frac{\lfloor\frac{n}{k}\rfloor}{d}\rfloor ⌊d⌊kn⌋⌋个 d d d的倍数
= ∑ d = 1 m i n ( ⌊ n k ⌋ , ⌊ m k ⌋ ) μ ( d ) ⌊ ⌊ n k ⌋ d ⌋ ⌊ ⌊ m k ⌋ d ⌋ =\sum _{d=1} ^{min(\lfloor \frac nk\rfloor, \lfloor \frac mk\rfloor)} \mu(d) \lfloor\frac{\lfloor\frac{n}{k}\rfloor}{d}\rfloor \lfloor\frac{\lfloor\frac{m}{k}\rfloor}{d}\rfloor =∑d=1min(⌊kn⌋,⌊km⌋)μ(d)⌊d⌊kn⌋⌋⌊d⌊km⌋⌋
线性筛筛出莫比乌斯函数,然后二维数论分块即可。
AC代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
#define vi vector<int>
#define vll vector<ll>
#define fi first
#define se second
const int maxn = 5e4 + 5;
const int mod = 1e9 + 7;
int a[maxn];
int u[maxn] , bk[maxn] , p[maxn] , cnt;
void init ()
{
u[1] = 1;
for (int i = 2 ; i < maxn ; i++){
if (!bk[i]){
p[++cnt] = i;
u[i] = -1;
}
for (int j = 1 ; j <= cnt && i * p[j] < maxn ; j++){
bk[i * p[j]] = 1;
if (i % p[j] == 0) {
u[i * p[j]] = 0;
break;
}else {
u[i * p[j]] = -u[i];
}
}
}
for (int i = 1 ; i < maxn ; i++) u[i] += u[i - 1];
return ;
}
ll solve (ll n , ll m)
{
ll ans = 0;
for (ll l = 1 , r ; l <= min(n , m) ; l = r + 1){
r = min(n / (n / l) , m / (m / l));
ans += (u[r] - u[l - 1]) * (n / l) * (m / l);
}
return ans;
}
int main()
{
ios::sync_with_stdio(false);
init();
int t; cin >> t;
while (t--){
ll a , b , c , d , k;
cin >> a >> b >> c >> d >> k;
// (a , c) (b , d)
ll ans = solve(b / k , d / k) - solve((a - 1) / k , d / k)
- solve(b / k , (c - 1) / k) + solve((a - 1) / k , (c - 1) / k);
cout << ans << endl;
}
return 0;
}