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{lfloorfrac{n}{i} floor}{i+j} floor[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)} floor[gcd(i,j)==1]\ ]

考虑(mu)函数的性质

[ans=sum_{i=1}^{sqrt{n}}sum_{j=1}^{i-1}lfloor frac{n}{i(i+j)} floorsum_{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)} floor\ ]

后面的考虑数论分块,枚举(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
",ans);
}
原文地址:https://www.cnblogs.com/lcxer/p/10561268.html