太神了。完全不会。。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