# 【BZOJ4652】循环之美（NOI2016）-杜教筛

$ans=\sum _{i=1}^{n}\sum _{j=1}^{m}\left[gcd\left(i,j\right)=1\right]\left[gcd\left(j,k\right)=1\right]$

$ans=\sum _{d|k}\mu \left(d\right)\sum _{i=1}^{n}\sum _{d|j}\left[gcd\left(i,j\right)=1\right]$

$ans=\sum _{d|k}\mu \left(d\right)\sum _{i=1}^{n}\sum _{j=1}^{⌊\frac{m}{d}⌋}\left[gcd\left(i,j\right)=1\right]\left[gcd\left(i,d\right)=1\right]$

$g\left(n,m,k\right)=\sum _{d|k}\mu \left(d\right)g\left(⌊\frac{m}{d}⌋,n,d\right)$

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll hashsiz=7000003;
ll n,m,k,limit;
ll prime[1100010],mu[1100010],summu[1100010];
ll hashlist[7000010]={0},hashval[7000010],inhashlist=0;
bool vis[1100010]={0};

void calc_mu()
{
mu[1]=1;
prime[0]=0;
for(ll i=2;i<=limit;i++)
{
if (!vis[i])
{
prime[++prime[0]]=i;
mu[i]=-1;
}
for(ll j=1;j<=prime[0]&&i*prime[j]<=limit;j++)
{
vis[i*prime[j]]=1;
if (i%prime[j]==0)
{
mu[i*prime[j]]=0;
break;
}
mu[i*prime[j]]=-mu[i];
}
}
summu[0]=0;
for(int i=1;i<=limit;i++)
summu[i]=summu[i-1]+mu[i];
}

ll hashfind(ll x)
{
ll pos=x%hashsiz;
while(hashlist[pos]&&hashlist[pos]!=x)
{
pos++;
if (pos>=hashsiz) pos-=hashsiz;
}
if (hashlist[pos]==x) return pos;
else return -1;
}

void hashinsert(ll x,ll val)
{
if (inhashlist>=hashsiz) return;
inhashlist++;
ll pos=x%hashsiz;
while(hashlist[pos]&&hashlist[pos]!=x)
{
pos++;
if (pos>=hashsiz) pos-=hashsiz;
}
hashlist[pos]=x;
hashval[pos]=val;
}

ll sum_mu(ll n)
{
if (n<=limit) return summu[n];
int pos=hashfind(n);
if (pos!=-1) return hashval[pos];
ll ans=1;
for(ll i=n;i>=2;i=n/(n/i+1))
{
ll l=max(n/(n/i+1)+1,2ll),r=i;
ans-=(r-l+1)*sum_mu(n/i);
}
hashinsert(n,ans);
return ans;
}

ll sum(ll n,ll m)
{
ll ans=0;
for(ll i=min(n,m);i>=1;i=max(n/(n/i+1),m/(m/i+1)))
{
ll l=max(n/(n/i+1),m/(m/i+1))+1,r=i;
ans+=(sum_mu(r)-sum_mu(l-1))*(n/i)*(m/i);
}
return ans;
}

ll calc(ll n,ll m,ll k)
{
ll ans=0;
if (!n||!m) return 0;
if (k==1) return sum(n,m);
for(ll i=1;i*i<=k;i++)
if (k%i==0)
{
ans+=mu[i]*calc(m/i,n,i);
if (i*i!=k) ans+=mu[k/i]*calc(m/(k/i),n,k/i);
}
return ans;
}

int main()
{
scanf("%lld%lld%lld",&n,&m,&k);
limit=1;
while(limit*limit*limit<=max(n,m)) limit++;
limit*=limit;
limit=max(limit,k);
calc_mu();
printf("%lld",calc(n,m,k));

return 0;
}
posted @ 2018-05-08 21:02  Maxwei_wzj  阅读(88)  评论(0编辑  收藏  举报