题目大意,从1到N,1 = X0, X1, X2, …, Xm = X中间可以分成很多数,另Xi < Xi+1 Xi 可以整除Xi+1 ,求最大长度m和m长度的链有多少条
思路:
很简单,只要把数分解质因数就可以了,最后链条的长度为质因数的个数,组合数为质因数个数的阶乘除以各自重复质因数的阶乘。还记得我发过的GCM和LCM反转的那道题吗,可以用pallord_rho算法分解质因数。
贴代码,第一个是最坑爹的,这一题会专门出数据坑Miller_Rabin算法,所以必须素数验证必须执行8次以上,不能用srand改变种子,否则你就是在和上帝玩骰子。
1 #include <iostream> 2 #include <algorithm> 3 #include <functional> 4 #include <time.h> 5 6 using namespace std; 7 typedef long long LL_INT; 8 9 bool Miller_Rabin(const LL_INT); 10 LL_INT witness(const LL_INT,const LL_INT,const LL_INT); 11 void Find_Factors(const LL_INT, int *const, const int); 12 LL_INT Pallord_Rho_Theorem(const LL_INT, const int); 13 LL_INT Multi_Function(LL_INT,const LL_INT); 14 LL_INT gcd(LL_INT, LL_INT); 15 16 static LL_INT factors[10000], factors_num[10000]; 17 static long long coe[23]; 18 static int factors_sum[10000]; 19 20 void Inivilize(void) 21 { 22 coe[1] = 1; 23 for (int i = 2; i <= 20; i++) 24 coe[i] = coe[i - 1] * i; 25 } 26 27 int main(void) 28 { 29 LL_INT x; 30 long long chains_sum; 31 int prime_sum, len, i; 32 33 Inivilize(); 34 while (~scanf("%lld", &x)) 35 { 36 prime_sum = 0; len = 0; 37 if (x <= 1) 38 printf("0 1 "); 39 else if (Miller_Rabin(x))//如果是素数,直接返回1 1 40 printf("1 1 "); 41 else 42 { 43 Find_Factors(x, &prime_sum, 120);//120是经验值 44 sort(factors, factors + prime_sum); 45 factors_num[0] = factors[0]; 46 memset(factors_sum, 0, sizeof(factors_sum)); 47 factors_sum[0] = 1; 48 for (i = 1; i < prime_sum; i++) 49 { 50 if (factors[i - 1] == factors[i]) 51 factors_sum[len]++; 52 else 53 { 54 factors_num[++len] = factors[i]; 55 factors_sum[len] = 1; 56 } 57 } 58 chains_sum = coe[prime_sum]; 59 for (int i = 0; i <= len; i++) 60 chains_sum /= coe[factors_sum[i]]; 61 printf("%d %lld ", prime_sum, chains_sum); 62 } 63 } 64 return 0; 65 } 66 67 bool Miller_Rabin(const LL_INT n) 68 { 69 if (n == 2) 70 return true;//如果是2,就不用判断了 71 else 72 { 73 for (int i = 0; i < 8; i++) 74 { 75 if (!(witness((LL_INT)(rand() % (n - 1)) + 1, n - 1, n) == 1)) 76 return false; 77 } 78 return true; 79 } 80 } 81 82 LL_INT witness(const LL_INT coe, const LL_INT level, const LL_INT n) 83 { 84 LL_INT y, x; 85 if (level == 0) 86 return 1; 87 x = witness(coe, level >> 1, n); 88 89 if (x == 0) 90 return 0; 91 y = (x*x) % n; 92 if (y == 1 && x != 1 && x != n - 1) 93 return 0;//费马小定理的运用 94 if (level % 2 == 1) 95 y = (coe*y) % n; 96 97 return y; 98 } 99 100 void Find_Factors(const LL_INT n, int *const len, const int times) 101 { 102 if (n == 1) 103 return; 104 else if (Miller_Rabin(n)) 105 factors[(*len)++] = n; 106 else 107 { 108 LL_INT p = n; 109 int c = times; 110 while (p >= n) 111 p = Pallord_Rho_Theorem(n, c--); 112 Find_Factors(p, len, times); 113 Find_Factors(n / p, len, times); 114 } 115 } 116 117 LL_INT Pallord_Rho_Theorem(const LL_INT n, const int c) 118 { 119 LL_INT x, y, k = 2, d; 120 x = y = rand() % n;//随意取一个随机数 121 122 for (int i = 1;; i++) 123 { 124 x = (Multi_Function(x, n) + c) % n; 125 d = gcd(n, (y - x + n) % n);//计算|y-x|与n的最大公因数 126 if (1 < d && d < n) 127 return d; 128 else if (y == x) 129 return n;//相当于这个因数分解是失败的,所以直接返回n让外层循环继续 130 else if (i == k)//brent判据,目的就是找到在偶数周期内找到gcd(x(k)-x(i/2)) 131 { 132 y = x;//重新y=x,定义循环 133 k <<= 1; 134 } 135 } 136 return n; 137 } 138 139 LL_INT Multi_Function(LL_INT x,const LL_INT mod) 140 { 141 LL_INT y = x, ans = 0; 142 while (y)//计算y=x^2的取模算法 143 { 144 if (y & 1) 145 ans = (ans + x) % mod; 146 x = (x << 1) % mod; 147 y >>= 1; 148 } 149 return ans; 150 } 151 152 LL_INT gcd(LL_INT a, LL_INT b) 153 { 154 if (b == 0) 155 return a; 156 return gcd(b, a%b); 157 }
挺慢的,其实直接用筛法更快,先把表打好,然后再一个一个选就可以了,用筛法的话可以到100ms以内
1 #include <iostream> 2 #include <functional> 3 #include <algorithm> 4 5 using namespace std; 6 7 static bool primes[(1 << 20) + 5]; 8 static int primes_set[500000]; 9 static long long fact[22]; 10 11 void Inivilize(int *const primes_sum) 12 { 13 int i, j; 14 primes[1] = primes[0] = 1; 15 for (i = 2; i <= 1 << 20; i++) 16 { 17 if (!primes[i]) primes_set[(*primes_sum)++] = i; 18 for (j = 2; j*i <= 1 << 20 && j <= i; j++) 19 { 20 if (primes[j] == 0) 21 primes[j*i] = 1; 22 } 23 } 24 fact[1] = 1; 25 for (i = 2; i <= 21; i++) 26 fact[i] = fact[i - 1] * i; 27 } 28 29 int main(void) 30 { 31 int x, last, i, tmp_div_sum, tmp_last, longest_length, primes_sum = 0, prime_num; 32 long long div_sum, chain_sum; 33 Inivilize(&primes_sum); 34 35 while (~scanf("%d", &x)) 36 { 37 if (x == 1) 38 printf("0 1 "); 39 else 40 { 41 div_sum = 1; last = x; longest_length = 0; 42 for (i = 0; last != 1; i++) 43 { 44 if (!primes[last]) 45 { 46 longest_length++; 47 break; 48 } 49 prime_num = primes_set[i]; 50 for (tmp_div_sum = 0, tmp_last = last; tmp_last%prime_num == 0;) 51 { 52 if (!tmp_last) break; 53 tmp_last /= prime_num; 54 tmp_div_sum++; longest_length++; 55 } 56 last = tmp_last; 57 if (tmp_div_sum) 58 div_sum *= fact[tmp_div_sum]; 59 } 60 chain_sum = fact[longest_length]; 61 chain_sum /= div_sum; 62 printf("%d %lld ", longest_length, chain_sum); 63 } 64 } 65 return 0; 66 }