【bzoj2671】Calc 数论

      太神了。完全不会。。o(╯□╰)o。

      首先来看一下怎样的ab满足条件:

      设d=gcd(a,b),a=du,b=dv,那么:

      a+b|ab → du+dv|d^2uv → u+v|duv,由gcd(u,v)=1 → gcd(u,v,uv)=1 → u+v|d。因此,在本题中就是要找到有多少对u,v,t,其中d=t(u+v),满足:

      b=dv=tv(u+v)<=n,如果枚举u,v答案即ΣuΣv[n/v(u+v)]*[gcd(u,v)=1]。

      显然v(u+v)<=n→v(v+1)<=n,因此我们知道v<n^0.5,那么可以枚举v,而右边的式子在v定下来的情况下可以分区间进行计算,那么关键就是求出在(x,y)中有多少与v互质的数。由μ函数的性质可以得到,这个值为:

      Σ(k|v) μ(k)(y/k-(x-1)/k)(这里/表示整除)

     于是就做完了。。但是如果将一个数的约数个数看做O(N^0.5)的话,时间复杂度为O(N^0.5*(N^0.5)^0.5*(N^0.5)^0.5)=O(N)!所以只能说约数的个数远远不足O(N^0.5)而且分区间的常数也是远远小于1的。。。关键这个算法跑2^31-1半秒都不到!!。

AC代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;

int n,cnt,tot,c[50005],a[50005],mu[50005]; bool bo[50005];
void pfs(){
	int i,j; memset(bo,1,sizeof(bo)); mu[1]=1;
	for (i=2; i<=50000; i++){
		if (bo[i]){ c[++cnt]=i; mu[i]=-1; }
		for (j=1; j<=cnt && i<=50000/c[j]; j++){
			bo[i*c[j]]=0;
			if (i%c[j]) mu[i*c[j]]=-mu[i]; else{
				mu[i*c[j]]=0; break;
			}
		}
	}
}
void dvs(int x){
	int i; tot=0;
	for (i=1; i*i<x; i++) if (!(x%i)){ a[++tot]=i; a[++tot]=x/i; }
	if (i*i==x) a[++tot]=i; sort(a+1,a+tot+1);
}
int main(){
	scanf("%d",&n); int i,j,x,y; ll ans=0; pfs();
	for (i=2; i<=n/(i+1); i++){
		dvs(i);
		for (x=1; x<i && i<=n/(x+i); x=y+1){
			y=min(n/(n/i/(x+i))/i-i,i-1); ll sum=0;
			for (j=1; a[j]<=y; j++) sum+=(ll)mu[a[j]]*(y/a[j]-(x-1)/a[j]);
			ans+=(ll)n/i/(x+i)*sum;
		}
	}
	printf("%lld\n",ans); return 0;
}

by lych

2016.2.4

你可能感兴趣的:(数论,莫比乌斯函数)