C - Mophues
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
第一發t:
題目的大體意思就是讓你求1<=i<=n和1<=j<=m範圍內gcd(i,j)的素因子個數小於p的(i,j)個數對。我們可以通過莫比烏斯反演在O(n)的複雜度計算i和j內gcd爲某個值的具體個數。再求莫比烏斯函數的時候我們也可以在數據範圍內所有的i值對應的因子個數預處理出來。
我猜測我可以枚舉gcd(i,j),判斷gcd(i,j)的素因子個數。然後把素因子個數小於p的累加。我還算了一下時間複雜度。是個調和級數求和乘以min(n,m)。大致爲n*log(n)的時間複雜度。考慮t後如果數據特別強就會t,果不其然t了。
for(int i=1;i<=minn;i++)
{
int nn=n,mm=m;
nn/=i;
mm/=i;
ll ans=0;
int minnn=min(nn,mm);
for(int j=1;j<=minnn;j++)
ans+=(ll)mu[j]*(nn/j)*(mm/j);
if(a[i]<=p)
res+=ans;
}
然後我又換了一種思路,我可以去計算每個值對應的範圍裏gcd的因子個數的莫比烏斯值的對應貢獻值。最後對這些值求一個前綴和。代表在在這個值範圍內,小於給定素因子個數值的全部莫比烏斯貢獻值。
設A(d):gcd(a, b)=d的有多少種
設B(j): gcd(a, b)是j的倍數的有多少種,易知B(j) = (n/j)*(m/j)
A(1) = μ(1)*B(1) + μ(2)*B(2) + μ(3)*B(3) + ... + μ(p1*p2...)*B(p1*p2...)
A(2) = μ(1)*B(1*2) + μ(2)*B(2*2) + μ(3)*B(3*2) + ... + μ(p1*p2..)*B(p1*p2..*2)
...
A(d) = μ(1)*B(1*d) + μ(2)*B(2*d) + μ(3)*B(3*d) + ... + μ(p1*p2..)*B(p1*p2..*d)
ans = A(1)+A(2)+...+A(d) = F(1)*B(1) + F(2)*B(2) + ... + F(p1*p2..)*B(p1*p2..)
於是可以枚舉公約數i{表示A(i)},利用篩法找出i的倍數j,i對B(j)的貢獻係數爲:F(j)+=μ(j/i)
總之,求出B(j)的總貢獻係數F(j)即可得答案:F(1)*B(1)+F(2)*B(2)+...+F(n)*B(n)
T=5*10^3,n最大爲5*10^5.還有預處理的過程還是有t的可能。
爲了優化我們對最後一步進行除法分塊。
求B[x]的過程就是 (n/x)*(m/x);
B[x+1] 爲 (n/(x+1))*(m/(x+1));
我們可以發現有很多 n/x ==n/(x+1)的情況,這時我們.順理當然想到除法分塊。
代碼如下:
#include<bits/stdc++.h>
typedef long long ll;
const int maxn = 5*1e5+5;
using namespace std;
int isprime[maxn],mu[maxn],prime[maxn],n,m,p,cnt,a[maxn],f[maxn][20];
void mobius(){
cnt=0;
mu[1]=1;
a[1]=0;
for(int i=2;i<maxn;i++)
{
if(!isprime[i]){
prime[cnt++]=i;
a[i]=1;
mu[i]=-1;
}
for(int j=0;j<cnt&&i*prime[j]<maxn;j++)
{
isprime[i*prime[j]]=1;
a[i*prime[j]]=a[i]+1;
if(i%prime[j])
mu[i*prime[j]]=-mu[i];
else
{
mu[i*prime[j]]=0;
break;
}
}
}
for(int i = 1; i < maxn; i++){
for(int j = i; j < maxn; j += i){
f[j][a[i]] += mu[j / i];
}
}
for(int i = 1; i < maxn; i++){
for(int j = 1; j < 20; j++){
f[i][j] += f[i][j - 1] ;
}
}
for(int i = 1; i < maxn; i++){
for(int j = 0; j < 20; j++){
f[i][j] += f[i - 1][j];
}
}
}
int main()
{
ll res,j;
int t;
mobius();
scanf("%d",&t);
while(t--)
{
scanf("%d %d %d",&n,&m,&p);
res=0;
int minn = min(n,m);
if(p>19)
p=19;
for(int i=1;i<=minn;i=j+1)
{
j = min(n/(n/i),m/(m/i));
res+=(n/j)*1ll*(m/j)*(f[j][p]-f[i-1][p]);
}
printf("%lld\n",res);
}
return 0;
}