- Step1 Problem
原题 给一个数n,输出sigma(gcd(i,j))1<=i<=j,i<=j<=n
- Step2 Ideas:
假设a、b(a<b)互质,那么gcd(a,b)=1,这样当i循环到a、j循环到b时就会向结果中+1,而i循环到2*a、j循环到2*b时就会向结果中+2(gcd(2*a,2*b)=2)...循环到k*a和k*b时就会向结果中+k。这样实际上引起结果变化的根源就在于各对互质的数,当i、j循环到他们自身或者自身的倍数时就会引起结果的改变,那么我们不妨先将每对互质的数对结果的贡献值算出来,最后将各对互质的数对结果的贡献累加起来就可以了。
假设和b互质的数有n个,也就是n对(?,b)(?和b互质),那么在i、j循环到?、b时结果会增加n,循环到(2*?,2*b)时结果就会增加2*n...当i、j循环到k*?、k*b时结果就会增加k*n。那么我们不妨用a[i]记录各种k、b在满足k*b=i时会增加多少结果,也就是说a[i]记录的是小于i的每个数与i最大公约数之和,那么最后我们要输出的就是a[2]+a[3]+...+a[N]。
至于找和b互质的数,就是计算b的欧拉函数的值,然后暴力循环k,并修改对应的a[k*b]即可,整体的复杂度是O(N*logN)的。
欧拉公式的延伸:小于n 与n互质的数的和 是euler(n)*n/2
- Step3 Code:
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<algorithm> 5 #include<bitset> 6 #include<cassert> 7 #include<cctype> 8 #include<math.h> 9 #include<cstdlib> 10 #include<ctime> 11 #include<deque> 12 #include<iomanip> 13 #include<list> 14 #include<map> 15 #include<queue> 16 #include<set> 17 #include<stack> 18 #include<vector> 19 #define lt k<<1 20 #define rt k<<1|1 21 #define lowbit(x) x&(-x) 22 #define lson l,mid,lt 23 #define rson mid+1,r,rt 24 using namespace std; 25 typedef long long ll; 26 typedef long double ld; 27 #define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); 28 #define mem(a, b) memset(a, b, sizeof(a)) 29 //#define int ll 30 const double pi = acos(-1.0); 31 const double eps = 1e-6; 32 const double C = 0.57721566490153286060651209; 33 const ll mod = 3e7; 34 const int inf = 0x3f3f3f3f; 35 const ll INF = 0x3f3f3f3f3f3f3f3f; 36 const int maxn = 4e6 + 5; 37 int phi[maxn]; 38 ll a[maxn]; 39 40 void prep() { 41 mem(a, 0); 42 for(int i = 1; i <= 4e6; i++) phi[i] = i; 43 for(int i = 2; i <= 4e6; i++) { 44 if(phi[i] == i) { 45 for(int j = i; j <= 4e6; j += i) { 46 phi[j] = phi[j] / i * (i - 1); 47 } 48 } 49 for(int j = 1; j * i <= 4e6; j++) a[j * i] += j * phi[i]; 50 } 51 for(int i = 1; i <= 4e6; i++) a[i] += a[i - 1]; 52 } 53 54 int main() { 55 prep(); 56 int n; 57 while(cin >> n && n) { 58 cout << a[n] << endl; 59 } 60 return 0; 61 }