bzoj2671:Calc
传送门
我是菜鸡不会写,我抄题解,我无耻发博客
我看的博客
虽然以上全都属实,但是这个题我确实也学到了些操作
首先先转化一下题意
对于\(a,b\)要求\(a+b|ab\),则\(gcd(a,b)!=1\)
我们考虑将\(d=gcd(a,b)\),则\(a=id,b=jd\)
我们知道\(gcd(i,j)==1\),所以\(i+j|ijd\)
因为我们知道\(gcd(i,j)==1\),所以\(gcd(i+j,ij)==1\)
那么我们要求的就要满足\(i+j|d,id<=n,jd<=n,gcd(i,j)==1\)
此时答案应该是
\[ans=\sum_{i=1}^{n}\sum_{j=1}^{i-1}\lfloor \frac{\lfloor\frac{n}{i}\rfloor}{i+j}\rfloor[gcd(i,j)==1]\\
\]
考虑\(i(i+j)<=n\)
\[ans=\sum_{i=1}^{\sqrt{n}}\sum_{j=1}^{i-1}\lfloor \frac{n}{i(i+j)}\rfloor[gcd(i,j)==1]\\
\]
考虑\(\mu\)函数的性质
\[ans=\sum_{i=1}^{\sqrt{n}}\sum_{j=1}^{i-1}\lfloor \frac{n}{i(i+j)}\rfloor\sum_{k|gcd(i,j)}\mu(k)\\
\]
设\(i=xk,j=yk\)
\[ans=\sum_{k=1}^{\sqrt{n}}\mu(k)\sum_{x=1}^{\sqrt{n}/k}\sum_{y=1}^{x-1}\lfloor \frac{n}{xk^2(x+y)}\rfloor\\
\]
后面的考虑数论分块,枚举\(k,x\)后\(\frac{n}{xk^2}\)的值就确定了,考虑对于\((x+y)\)的值数论分块
据说总复杂度是\(O(n^{\frac{4}{3}})\),表示不会证
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
void read(int &x) {
char ch; bool ok;
for(ok=0,ch=getchar(); !isdigit(ch); ch=getchar()) if(ch=='-') ok=1;
for(x=0; isdigit(ch); x=x*10+ch-'0',ch=getchar()); if(ok) x=-x;
}
#define rg register
const int maxn=1e6+10;long long ans;
int n,m,mu[maxn],pri[maxn],tot;bool vis[maxn];
void prepare(int n)
{
mu[1]=1;
for(rg int i=2;i<=n;i++)
{
if(!vis[i])pri[++tot]=i,mu[i]=-1;
for(rg int j=1;j<=tot&&pri[j]*i<=n;j++)
{
vis[pri[j]*i]=1;
if(!(i%pri[j]))break;
else mu[i*pri[j]]=-mu[i];
}
}
}
long long solve(int k)
{
long long ans=0;
for(rg int x=1;x<=m/k;x++)
{
int g=n/(x*k*k),now=x<<1;if(!g)break;
for(rg int i=x+1,j;i<now;i=j+1)
{
if(!(g/i))break;
j=min(g/(g/i),now-1);
ans=(ans+1ll*(g/i)*(j-i+1));
}
}
return ans;
}
int main()
{
read(n),m=sqrt(n),prepare(m);
for(rg int k=1;k<=m;k++)ans+=mu[k]*solve(k);
printf("%lld\n",ans);
}