【欧拉函数】欧拉函数前缀和
转自http://www.cnblogs.com/chanme/p/4457200.html
H. Game
Alice likes to play games. One day she meets such a game. There are N * N switches arranged in an N * N array. Pressing a switch would change its state, from off to onor from on to off. In addition, if a switch at row r and column c is pressed, then all switches with coordinate (k * r, k * c) will change state, with integer k>1. Initially all switches are off.
For example, in the picture above, white buttons represent switches turned off and colored ones represent switches turned on. Initially all buttons are white. If the button at (2,2) is pressed, then buttons at (2,2), (4,4) will change state(represented with orange color). And if one presses the button (2,1), buttons at (2,1) and (4,2) will change from off toon(represented with gray color).
The goal of the game is to turn on all the switches (i.e. when you finish the game, all the switches must be at the state of on) and the player must do that with as few presses as possible. Now Alice would like your help.
Input
The first line of input file is an integer T, the number of test cases. T lines follow, each contain an integer N, the dimension of the array in one game.
1≤T≤50,1≤N≤10^9
Output
Output consists of T lines. Each line contains an integer, the minimum number of presses for the corresponding test case.
Sample input and output
Sample Input |
Sample Output |
2 2 3 |
3 7 |
Hint
For test case 1, press (1,1) (1,2) (2,1). For test case 2, press (1,1) (1,2) (1,3) (2,1) (2,3) (3,1) (3,2).
题意:给你一个n*n的矩阵的灯泡,当你按下(r,c)的灯泡的时候,所有的(k*r,k*c)的灯泡都会被点亮,问最小按的灯泡数使得所有的灯泡被点亮。
问题的模型可以转换为 有多少对(x,y)使得gcd(x,y)=1 (1<=x,y<=n)这是一个非常典型的gcd问题,如果我们令y<x,那么当x=k的时候,就有phi(k)个y使得 (x,y)=1,显然x可以取遍1-n,所以答案是(phi(1)+phi(2)+...+phi(n))*2-1(假设phi(1)=1)
如果我们记M(n)为有多少对(x,y)使得gcd(x,y)=1 (1<=x,y<=n)的个数。
那么有
有多少对(x,y)使得gcd(x,y)=1 (1<=x,y<=n)的个数 ==> M(n)
有多少对(x,y)使得gcd(x,y)=2 (1<=x,y<=n)的个数 ==> 有多少对(x,y)使得gcd(x,y)=1 (1<=x,y<=n/2)的个数 ==> M(n/2)
有多少对(x,y)使得gcd(x,y)=3 (1<=x,y<=n)的个数 ==> 有多少对(x,y)使得gcd(x,y)=1 (1<=x,y<=n/3)的个数 ==> M(n/3)
....
有多少对(x,y)使得gcd(x,y)=n (1<=x,y<=n)的个数 ==> 有多少对(x,y)使得gcd(x,y)=1 (1<=x,y<=n/n)的个数 ==> M(n/n)
(以上的M/k都是整数除法)
如果我们把上面的式子的左边加起来,再把右边也加起来,我们会得到一个非常重要的等式,即:
n^2=M(n)+M(n/2)+...M(n/n) ==> sigma(M(n/i),i=1...n) = n*n
注意到 n/i 不同的答案只有O(sqrt(n))个,所以有很多M(n/i)其实是相等的,不需要重复计算,于是一个递归求解的思路就出来了。首先我先预处理出[1,10^6]以内的M(k),然后记忆化搜索,那个失效的链接里给出的一个结论就是这样去计算的话复杂度是O(n^(2/3)),于是就解决了这个欧拉函数前缀和的问题。下面的代码是出这道题的同学给出来的程序,我在这里也直接贴出来了。
1 #include <iostream> 2 #include <stdio.h> 3 #include <algorithm> 4 #include <string.h> 5 #include <vector> 6 #include <map> 7 #include <math.h> 8 #include <iomanip> 9 #include <string> 10 #include <set> 11 #include <cassert> 12 #include <queue> 13 using namespace std ; 14 #define rep(i,a,b) for(int i=(int)(a);i<(int)(b);++i) 15 #define rrep(i,b,a) for(int i=(int)(b);i>=(int)(a);--i) 16 #define clr(a,x) memset(a,(x),sizeof(a)) 17 #define ll long long 18 #define ull unsigned ll 19 #define eps 1e-8 20 #define mp make_pair 21 #define lson l,m,rt<<1 22 #define rson m+1,r,rt<<1|1 23 #define ld long double 24 const int N = 1e9; 25 const int mod = 1313131; 26 const int maxn = 1000000; 27 28 ll key[mod], value[mod]; 29 ll phi[maxn]; 30 bool ispe[maxn]; 31 void pre_init() 32 { 33 rep(i,1,maxn) phi[i] = i, ispe[i] = true; 34 rep(i,2,maxn) if (ispe[i]) { 35 phi[i] = i - 1; 36 for(int j = i + i; j < maxn; j += i) { 37 phi[j] = phi[j] / i * (i - 1); 38 ispe[j] = false; 39 } 40 } 41 rep(i,1,maxn) phi[i] += phi[i-1]; 42 rep(i,1,maxn) phi[i] = 2 * phi[i] - 1; 43 } 44 45 void Add(ll n,ll val) 46 { 47 int k = n % mod; 48 while (key[k] != -1) { 49 ++k; 50 if (k == mod) k = 0; 51 } 52 key[k] = n; value[k] = val; 53 } 54 55 ll Query(ll n) 56 { 57 int k = n % mod; 58 while (key[k] != -1) { 59 if (key[k] == n) return value[k]; 60 ++k; 61 if (k == mod) k = 0; 62 } 63 return -1; 64 } 65 66 ll dfs(ll n) 67 { 68 assert(n >= 1 && n <= N); 69 if (n < maxn) return phi[n]; 70 // else if (n == 2) return 3; 71 // else if (n == 3) return 7; 72 ll ret = Query(n); 73 if (ret != -1) return ret; 74 ret = n * n; 75 for(int x = 2, y; x <= n; ++x) { 76 y = n / (n / x); 77 ret -= dfs(n / x) * (y - x + 1); 78 x = y; 79 } 80 Add(n,ret); 81 return ret; 82 } 83 84 void Getinput() 85 { 86 freopen("in.txt","w",stdout); 87 int T = 50; printf("%d\n",T); 88 int x = 1; 89 int * a = new int[T]; 90 rep(i,0,T) { 91 a[i] = x; 92 x += N / (rand()%50 + 1); 93 x = x % N + 1; 94 } 95 random_shuffle(a,a+T); 96 a[rand()%T] = N; 97 rep(i,0,T) printf("%d\n",a[i]); 98 delete [] a; 99 } 100 101 int main() 102 { 103 //Getinput(); return 0; 104 #ifdef ACM 105 freopen("in.txt","r",stdin); 106 //freopen("out.txt","w",stdout); 107 freopen("fast_out.txt","w",stdout); 108 #endif // ACM 109 pre_init(); 110 clr(key,-1); 111 int T; cin >> T; 112 while (T--) { 113 int n; 114 scanf("%d",&n); 115 assert(n >= 1 && n <= N); 116 printf("%lld\n",dfs(n)); 117 } 118 return 0; 119 }