设A(d):gcd(a, b)=d的有多少种
设B(j): gcd(a, b)是j的倍数的有多少种,易知B(j) = (n/j)*(m/j)
B(j)=A(j)+A(2*j)+A(3*j)+……
由莫比乌斯反演得:
A(j)=μ(1)*B(j)+μ(2)*B(2*j)+μ(3)*B(3*j)+……
分块加速思想
你可以再纸上模拟一下:设d在[i, n/(n/i)]的区间上,则该区间内所有的n/d都是一样的。
hdu 2841 Visible Trees
hdu 1695 GCD
hdu 4746 Mophues
前两道可用容斥原理解
hdu 2841 Visible Trees
求两个数段内互质对的个数。
<pre name="code" class="cpp">#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define ll __int64
using namespace std;
#define N 100000
int mib[N+10];
int f[N+10];
bool vis[N+10];
void mobi(){
int i,j;
for(i=1;i<=N;i++) mib[i]=1;
for(i=2;i<=N;i++){
if(vis[i]) continue;
for(j=i;j<=N;j+=i){
vis[j]=1;
if((j/i)%i==0){
mib[j]=0;
continue;
}
if(mib[j]==0) continue;
mib[j]=-mib[j];
}
}
f[0]=mib[0];
for(i=1;i<=N;i++)
f[i]=f[i-1]+mib[i];
}
int main()
{
int t,b,d,i,j;
scanf("%d",&t);
mobi();
while(t--)
{
scanf("%d%d",&b,&d);
if(b<d)
swap(b,d);
ll ans=0;
for(i=1;i<=d;i=j+1)
{
j=min(b/(b/i),d/(d/i));
ans+=(f[j]-f[i-1])*((ll)(b/i)*(d/i));
}
printf("%I64d\n",ans);
}
}
hdu 1695 GCD
和上道题,有些小差别,gcd(a,b) 和 gcd(b,a)属于一种。
<pre name="code" class="cpp">#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define ll __int64
using namespace std;
#define N 100000
int mib[N+10];
int f[N+10];
bool vis[N+10];
void mobi(){
int i,j;
for(i=1;i<=N;i++) mib[i]=1;
for(i=2;i<=N;i++){
if(vis[i]) continue;
for(j=i;j<=N;j+=i){
vis[j]=1;
if((j/i)%i==0){
mib[j]=0;
continue;
}
if(mib[j]==0) continue;
mib[j]=-mib[j];
}
}
f[0]=mib[0];
for(i=1;i<=N;i++)
f[i]=mib[i]+f[i-1];
}
int main()
{
int t,a,b,c,d,k,i,j;
scanf("%d",&t);
int res=0;
mobi();
while(t--)
{
res++;
scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
if(k==0)
{
printf("Case %d: 0\n",res);
continue;
}
b/=k,d/=k;
if(b<d)
swap(b,d);
ll ans=0;
for(i=1;i<=d;i=j+1)
{
j=min(b/(b/i),d/(d/i));
ans+=(ll)(f[j]-f[i-1])*((ll)(2*(ll)(b/i)*(d/i))+d/i-(ll)(d/i)*(d/i))/2;
}
printf("Case %d: %I64d\n",res,ans);
}
}
hdu 4746 Mophues
较难:
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<iostream>
#define ll __int64
using namespace std;
#define N 500000
int mib[N+10];
int f[N+10][22];
bool vis[N+10];
int num[N+10];
int h[N+10];
int cal(int n,int x)
{
int ans=0;
while(n%x==0)
{
ans++;
n/=x;
}
return ans;
}
int main()
{
int i,j,k;
memset(num,0,sizeof(num));
for(i=0;i<=N;i++)
h[i]=1;
for(i=2;i<=N;i++)
{
if(num[i]) continue;
for(j=i;j<=N;j+=i)
{
int t=cal(j,i);
num[j]+=t;
if(t>1) h[j]=0;
else h[j]=-h[j];
}
}
memset(f,0,sizeof(f));
for(i=1;i<=N;i++)
{
for(j=i;j<=N;j+=i)
{
f[j][num[i]]+=h[j/i];
}
}
for(i=1;i<=N;i++)
for(j=1;j<=20;j++)
f[i][j]+=f[i][j-1];
for(i=0;i<=20;i++)
for(j=1;j<=N;j++)
f[j][i]+=f[j-1][i];
int q;
scanf("%d",&q);
while(q--)
{
int n,m,p;
scanf("%d%d%d",&n,&m,&p);
if(p>=20)
{
cout<<(ll)n*m<<endl;
continue;
}
if(n>m) swap(n,m);
ll ans=0;
for(i=1;i<=n;i=j+1)
{
j=min(n/(n/i),m/(m/i));
ans+=(ll)(f[j][p]-f[i-1][p])*(n/i)*(m/i);
}
cout<<ans<<endl;
}
}