原理很简单
题解我就不自己写了……
做这题的时候,懂得了一个非常重要的转化:求(x, y) = k, 1 <= x, y <= n
的对数等于求(x, y) = 1, 1 <= x, y <= n/k
的对数!所以,枚举每个质数p
(线性筛素数的方法见:线性时间内筛素数和欧拉函数),然后求(x, y) = 1, 1 <= x, y <= n/p
的个数。
那(x, y) = 1
的个数如何求呢?其实就是求互质的数的个数。在[1, y]
和y
互质的数有phi(y)
个,如果我们令x < y
,那么答案就是sigma(phi(y))
。因为x, y
是等价的,所以答案*2,又因为(1, 1)
只有一对,所以-1。最终答案为sigma(sigma(phi(n/prime[i])) * 2 - 1)
。
提交的时候我都快疯了
在这个题中,我们应该需要注意两点:
一、bzoj上215错误会被报为wa,坑爹啊
二、在数组开的很大的情况下,能不用int64的就不用
因为内存占用过大会大致速度变慢
代码:
1 var i,n,tot:longint; 2 ans:int64; 3 phi:array[0..10000000] of int64; 4 p:array[0..10000000] of longint; 5 procedure get; 6 var i,j:longint; 7 begin 8 fillchar(phi,sizeof(phi),0); 9 tot:=0; 10 phi[1]:=0; 11 for i:=2 to n do 12 if phi[i]=0 then 13 begin 14 phi[i]:=i-1;inc(tot);p[tot]:=i; 15 j:=i+i; 16 while j<=n do 17 begin 18 if phi[j]=0 then phi[j]:=j; 19 phi[j]:=(phi[j] div i)*(i-1); 20 inc(j,i); 21 end; 22 end; 23 end; 24 procedure main; 25 begin 26 readln(n); 27 get; 28 for i:=2 to n do inc(phi[i],phi[i-1]); 29 ans:=0; 30 for i:=1 to tot do inc(ans,2*phi[n div p[i]]+1); 31 writeln(ans); 32 end; 33 begin 34 main; 35 end. 36
(我习惯把phi[1]定为0,因为这比较符合逻辑。)