题意:定义一个数字为lucky时,当且仅当这个数字的质因数的个数<=p(输入的数字)时,问在 a∈[1,n],b∈[1,m] a ∈ [ 1 , n ] , b ∈ [ 1 , m ] 有多少对 (a,b)使得gcd(a,b)=k ( a , b ) 使 得 g c d ( a , b ) = k 且 k k 为lucky数字
题解:
我们设:
F(n)=∑n|df(d) F ( n ) = ∑ n | d f ( d )
解释一下: f(d) f ( d ) 为计算在给定区间内gcd(a,b)=d的对数,F(n)表示在给定区间内gcd为n的倍数的有多少对
由莫比乌斯反演的公式可以得到如下公式:
f(n)=∑x|du(dx)F(d) f ( n ) = ∑ x | d u ( d x ) F ( d )
我们可以很轻松的得到 F(d)=⌊nd⌋⌊md⌋ F ( d ) = ⌊ n d ⌋ ⌊ m d ⌋ ;
我们假设k的质因子数目不超过p,即满足题意则:
ans=∑kf(k) a n s = ∑ k f ( k )
把 F(n) F ( n ) 带入 f(n) f ( n ) 然后带入 ans a n s 中可以得到
ans=∑k∑k|nu(⌊nk⌋)⌊nk⌋⌊mk⌋ a n s = ∑ k ∑ k | n u ( ⌊ n k ⌋ ) ⌊ n k ⌋ ⌊ m k ⌋
将上式整理可以得到
ans=∑k|nu(nk)∑k⌊nk⌋⌊mk⌋ a n s = ∑ k | n u ( n k ) ∑ k ⌊ n k ⌋ ⌊ m k ⌋
此时总的复杂度为总的复杂度就为 o(q∗n∗logn+n) o ( q ∗ n ∗ l o g n + n ) ,大概获得一个TLE
我们观察上面的 ans a n s 为一次询问的复杂度,如果每次询问的复杂度为 log(n)或者(√n) l o g ( n ) 或 者 ( n ) 还可以接受。
我们考虑如何优化加速 ans a n s 的求解
首先我们考虑 ∑k|nu(nk) ∑ k | n u ( n k ) :
是否可以通过前缀和的形式进行优化,当然可以,我们通过预处理前缀和 sum(n)=∑k|nu(nk) s u m ( n ) = ∑ k | n u ( n k ) ,根据题意我们要保证 k k 的质因子数目不超过p,所以我们维护前缀和的同时增加一维来记录质因子的数目,数据范围为最多有 log(500000)=19 l o g ( 500000 ) = 19 个质因子。这样我们就可以 O(1) O ( 1 ) 的得到前半部分的值,然后我们考虑后半部分能不能优化,由整数分块的知识可以得到在区间 [i,⌊n⌊ni⌋⌋] [ i , ⌊ n ⌊ n i ⌋ ⌋ ] 值是相同的,所以我们后半部分的计算时间也可以优化到根号时间内来完成。这样总的时间复杂度为 O(n+log(n)+q(√n)) O ( n + l o g ( n ) + q ( n ) ) 这样就不会被制裁了。
注:写的不好看,谢谢阅读了。
accode: a c c o d e :
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll inf = 0x3f3f3f3f;
typedef long double ld;
#define met(a, b) memset(a, b, sizeof(a))
#define rep(i, a, b) for(int i = a; i <= b; i++)
#define per(i, a, b) for(int i = a; i >= b; i--)
#define fi first
#define pb push_back
#define mpi pair<int, int>
#define mp make_pair
inline ll mul(ll x, ll y, ll mod) {ll res = (x * y - (ll)(ld)(x / mod * y + 1e-8) * mod); return res < 0 ? res + mod : res;}
const double PI = acos(-1.0);
const int maxn = 5e5 + 10;
const int mod = 1e9+7;
int T, k, a, b, c, d;
int mu[maxn + 10], prime[maxn + 10], cnt[maxn + 10];
bool check[maxn + 10];
void Moblus() {
met(check,false);
mu[1] = 1;
int tot = 0;
for(int i = 2; i <= maxn; i++) {
if(!check[i]) {
prime[tot++] = i;
mu[i] = -1;
cnt[i] = 1;
}
for(int j = 0; j < tot; j++) {
if(i * prime[j] > maxn) break;
check[i * prime[j]] = true;
cnt[i * prime[j]] = cnt[i] + 1;
if(i % prime[j] == 0) {
mu[i * prime[j]] = 0;
break;
} else {
mu[i * prime[j]] = -mu[i];
}
}
}
}
ll sum[40][maxn + 10];
void init() {
Moblus();
met(sum, 0);
for(int i = 1; i < maxn; i++) {
for(int j = i; j < maxn; j += i) {
sum[cnt[i]][j] += mu[j / i];
}
}
for(int j = 1; j < maxn; j++) {
for(int i = 1; i <= 20; i++) {
sum[i][j] += sum[i - 1][j];
}
}
for(int j = 1; j < maxn; j++) {
for(int i = 0; i <= 20; i++) {
sum[i][j] += sum[i][j - 1];
}
}
}
ll cal(int n, int m, int p) {
if(p >= 20) printf("%lld\n", (ll)n * m);
else {
ll ans = 0;
ll mx = min(n, m);
for(int i = 1; i <= mx;) {
ll a = n / i, b = m / i;
ll key = min(n / a, m / b);
ans += (sum[p][key] - sum[p][i - 1]) * a * b;
i = key + 1;
}
printf("%lld\n", ans);
}
}
int n, m, p;
int main() {
int cas = 1;
init();
scanf("%d", &T);
while(T--) {
scanf("%d%d%d", &n, &m, &p);
cal(n, m, p);
}
return 0;
}