HDU4746 Mophues
题目地址
Description
As we know, any positive integer C ( C >= 2 ) can be written as the multiply of some prime numbers:
C = p1×p2× p3× ... × pk
which p1, p2 ... pk are all prime numbers.For example, if C = 24, then:
24 = 2 × 2 × 2 × 3
here, p1 = p2 = p3 = 2, p4 = 3, k = 4
Given two integers P and C. if k<=P( k is the number of C's prime factors), we call C a lucky number of P.
Now, XXX needs to count the number of pairs (a, b), which 1<=a<=n , 1<=b<=m, and gcd(a,b) is a lucky number of a given P ( "gcd" means "greatest common divisor").
Please note that we define 1 as lucky number of any non-negative integers because 1 has no prime factor.
Input
The first line of input is an integer Q meaning that there are Q test cases.
Then Q lines follow, each line is a test case and each test case contains three non-negative numbers: n, m and P (n, m, P <= 5×10 5. Q <=5000).
Output
For each test case, print the number of pairs (a, b), which 1<=a<=n , 1<=b<=m, and gcd(a,b) is a lucky number of P.
Sample Input
2
10 10 0
10 10 1
Sample Output
63
93
Solution
神仙结论。不过其实不难想。。。但是我不看题解很难想到。
首先大力推式子但是我懒得写推导在这里了意会一下吧,写到这道题应该也不难推到这里=_=
\[\sum_{T=1}^n\frac{n}{T}\frac{m}{T}\sum_{d|T}\mu(\frac{T}{d})[h(d)<=p]
\]
但是这个\(h(d)\)怎么解决真的不知道。。。p是每次给定的啊。。。
于是抄题解,领会到了神仙们的神仙思维多么强大。
\(2^{18}\)就大于\(500000\)了,所以\(h(i)\)一定都小于18,对于p大于18的,表达式一定恒为真,前缀和预处理前p<=18的情况就好了。。
#include <bits/stdc++.h>
using namespace std;
const int N = 500010;
#define ll long long
int mu[N], T, p[N], cnt;
int h[N], sum[20][N], P;
bool vis[N];
ll n, m;
void init() {
mu[1] = 1;
for(int i = 2; i < N; ++i) {
if(!vis[i]) p[++cnt] = i, mu[i] = -1, h[i] = 1;
for(int j = 1; j <= cnt && i * p[j] < N; ++j) {
vis[i * p[j]] = 1;
h[i * p[j]] = h[i] + 1;
if(i % p[j] == 0) break;
mu[i * p[j]] = -mu[i];
}
}
for(int i = 1; i < N; ++i)
for(int j = i; j < N; j += i)
sum[h[i]][j] += mu[j / i];
for(int k = 0; k <= 18; ++k)
for(int i = 1; i < N; ++i)
sum[k][i] += sum[k][i - 1];
for(int j = 1; j <= 18; ++j)
for(int i = 1; i < N; ++i)
sum[j][i] += sum[j - 1][i];
}
ll calc(ll n, ll m, ll lim) {
ll ans = 0;
if(n > m) swap(n, m);
for(ll l = 1, r; l <= n; l = r + 1) {
r = min(n / (n / l), m / (m / l));
ans += 1ll * (sum[lim][r] - sum[lim][l - 1]) * (n / l) * (m / l);
}
return ans;
}
int main() {
init();
scanf("%d", &T);
while(T--) {
scanf("%lld%lld%d", &n, &m, &P);
if(P > 18) printf("%lld\n", n * m);
else printf("%lld\n", calc(n, m, P));
}
return 0;
}