Mathematics:X-factor Chains(POJ 3421)

              

               X链条

  题目大意,从1到N,1 = X0X1X2, …, 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 }

原文地址:https://www.cnblogs.com/Philip-Tell-Truth/p/5010929.html