思路:
对于
因为
我们要求的式子就是
把
(然后我就反演了半天,最后被reflash和mrazer一眼秒了)
因为
关于F函数,因为
(我原本没预处理,暴力算的,在uoj跑76分,问了reflash才知道可以
前面部分根据d的取值分块算,现在问题就在于如何快速计算
%一发reflash
(这个我真没想到,卡在这里挺久的,因为没有把
设
则
两个互质条件合并一下,就是
非常像杜教筛的处理方法,记忆化即可
小范围的时候可以暴力
代码:
#include<cstdio>
#include<iostream>
#define LL long long
#define ui unsigned int
using namespace std;
int gcd(int x,int y){return y?gcd(y,x%y):x;}
int n,m,k;
const int lim=2000000;
int prime[lim/10+5],f[2005],py[2005];
ui pr[2005];
short mu[lim+5],sum[lim+5];
char vis[lim+5];
void init()
{
mu[1]=1;
int t,i,j;
for (i=2;i<=lim;++i)
{
if (!vis[i])
prime[++prime[0]]=i,
mu[i]=-1;
for (j=1;j<=prime[0]&&(t=i*prime[j])<=lim;++j)
{
vis[t]=1;
if (i%prime[j]) mu[t]=-mu[i];
else break;
}
}
for (int i=1;i<=lim;++i) sum[i]=sum[i-1]+mu[i];
for (int i=2;i<=k;++i)
if (k%i==0&&mu[i]) pr[++pr[0]]=i;
for (int i=1;i<=k;++i)
{
f[i]=f[i-1];py[i]=py[i-1];
if (gcd(i,k)==1) ++f[i],py[i]+=mu[i];
}
}
LL S(ui m){return 1LL*(m/k)*f[k]+f[m%k];}
const int P=999983;
struct node{
int tot,first[P+5],next[200005],ha[200005];
ui data[200005];
void push(ui n,int t)
{
int p=n%P;
ha[++tot]=t;
data[tot]=n;
next[tot]=first[p];
first[p]=tot;
}
int get(ui n)
{
for (int i=first[n%P];i;i=next[i])
if (data[i]==n) return i;
return 0;
}
}mp_mu,mp;
int cal_mu(ui n)
{
if (n<=lim) return sum[n];
int id=mp_mu.get(n);
if (id) return mp_mu.ha[id];
int t=1;
for (ui last,p,i=2;i<=n;i=last+1)
last=n/(p=n/i),
t-=(last-i+1)*cal_mu(p);
mp_mu.push(n,t);
return t;
}
int cal(ui n)
{
if (n<=k) return py[n];
int id=mp.get(n);
if (id) return mp.ha[id];
int t=cal_mu(n);
for (int i=1;i<=pr[0]&&pr[i]<=n;++i)
t-=mu[pr[i]]*cal(n/pr[i]);
mp.push(n,t);
return t;
}
main()
{
scanf("%d%d%d",&n,&m,&k);
init();
LL ans=0;
int ls=0,rs,ll=min(n,m);
for (ui p,q,last,i=1;i<=ll;i=last+1)
last=min((ui)n/(p=n/i),(ui)m/(q=m/i)),
ans+=((rs=cal(last))-ls)*S(q)*p,
ls=rs;
printf("%lld\n",ans);
}