您的位置:首页 > 理论基础 > 计算机网络

hdu4746:2013杭州网络赛I 莫比乌斯反演

2015-09-23 21:10 447 查看
题意:

有5000组询问,每组询问求有多少i,j满足i∈[1,n],j∈[1,m]且gcd(i,j)的质因子数目<=p。 n,m<=500000

思路:

首先预处理出每个数的质因子数目分别等于多少,则问题转化为求给定区间内,gcd等于某一堆数的i,j有多少组

发现很像一个基础莫比乌斯反演题:hdu1695。但是此题在某组询问中可能要处理很多个gcd,所以需要进行一些预处理

我们首先筛出每个数的莫比乌斯函数和它的质因子个数

通过容斥的公式可以看出如果要求的gcd为d,那么d*i的倍数对答案的贡献为 mobi[i]。

这样就可以预处理出每个p对应位置的莫比乌斯函数系数之和p[19][500000]

注意这里容易想到p是小于等于18的,因为50W之内的数最多有18个质因子(2^19>500000)

然后对p数组求前缀和,可以使单组查询复杂度变为p*sqrt(n),具体为什么是分块sqrt(n)可以参考hdu1695的题解。交了一发超时了。。

还以为写搓了,后来发现可以再求一次前缀和。。这样单组查询变成了sqrt(n),终于过了

代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn=500000;
int mb[maxn+10];
int notprime[maxn+10];
int num[maxn+10];
int prime[maxn];
vector<int>v[20];
int np=0;
void setprime()
{
memset(notprime,0,sizeof(notprime));
mb[1]=1;
num[1]=0;
for(int i=2; i<=maxn; i++)
{
if(!notprime[i])
{
prime[np++]=i;
num[i]=1;
mb[i]=-1;
}
for(int j=0; j<np&&i*prime[j]<=maxn; j++)
{
notprime[i*prime[j]]=1;
num[i*prime[j]]=num[i]+1;
//v[num[i]+1].push_back(i*prime[j]);
if(i%prime[j]==0)
{
mb[i*prime[j]]=0;
break;
}
else
{
mb[i*prime[j]]=-mb[i];
}
}
}
}
int p[20][500050];
int sum[20][500050];
int f[20][500050];
int main()
{
freopen("in.txt","r",stdin);
setprime();
for(int i=1; i<=maxn; i++)
{
for(int j=1; i*j<=maxn; j++)
{
p[num[i]][i*j]+=mb[j];
}
}
for(int i=0; i<=18; i++)
{
sum[i][0]=0;
for(int j=1; j<=maxn; j++)
{
sum[i][j]=sum[i][j-1]+p[i][j];
}
}
for(int i=1; i<=maxn; i++)
{
f[0][i]=sum[0][i];
for(int j=1;j<=18;j++)
{
f[j][i]=f[j-1][i]+sum[j][i];
}
}
int n,m,k,q;
scanf("%d",&q);
while(q--)
{
long long ans=0;
scanf("%d%d%d",&n,&m,&k);
if(k>18)
{
printf("%I64d\n",(long long)n*m);
continue;
}
if(n>m)
swap(n,m);
for(int j=1; j<=n; j++)
{
int to=min(n/(n/j),m/(m/j));
ans+=(long long)(n/j)*(m/j)*(f[k][to]-f[k][j-1]);
j=to;
}
printf("%I64d\n",ans);
}
return 0;
}


View Code
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: