数论

poj 2417: baby-step giant-step的模板题

  hash用map实现会超时...所以我就手动模拟了一个模除hash...不会hash真受伤...

  有一个纠结的地方是,我推出来应该是不用从0循环到m,只用到m-1就行了。但是run到m-1的代码挂了。自己测试了一下,发现“8 2 4”这组数据过不了。

  算了一下,发现这组数据的特别在于它求出来的逆元是0. 所以我们需要特判逆元为0的情况。至于为什么run到m就行了,暂时还不清楚。

  (这题是离散对数的简化版。等有时间了去真正的研究一下离散对数。)

 1 //13440960    ooyyloo    2417    Accepted    1924K    63MS    G++    2107B    2014-09-14 16:27:24
 2 #include <cstdio>
 3 #include <iostream>
 4 #include <cmath>
 5 #include <algorithm>
 6 #define pb push_back
 7 #define mp make_pair
 8 #define rep(i,n) for(int i=0;i<n;++i)
 9 #define For(i,l,r) for(int i=l;i<=r;++i)
10 #define dbg(x) cout<<#x<<"="<<x<<endl
11 using namespace std;
12 typedef long long LL;
13 const int maxn = 100007;
14 LL ksm(LL a, LL n, LL p)
15 {
16     LL ret = 1;
17     for (a %= p; n; n >>= 1)
18     {
19         if (n & 1) ret = ret * a % p;
20         a = a * a % p;
21     }
22     return ret;
23 }
24 
25 int ta[maxn], lin[maxn], pos; LL sd[maxn], ssd[maxn];
26 void biu(LL s, LL t, LL i) { ++pos; lin[pos] = ta[s]; ta[s] = pos; sd[pos] = t; ssd[pos] = i; }
27 
28 LL p, b, n;
29 LL ans;
30 void insert(LL tp, LL i)
31 {
32     for (LL i = ta[tp % maxn]; i; i=lin[i])
33         if (sd[i] == tp)
34         {
35             ssd[i] = min(ssd[i], i);
36             return;
37         }
38     biu(tp % maxn, tp, i);
39 }
40 bool find(LL tp, LL &f)
41 {
42     for (LL i = ta[tp % maxn]; i; i=lin[i])
43         if (sd[i] == tp)
44         {
45             f = ssd[i];
46             return 1;
47         }
48     return 0;
49 }
50 void init()
51 {
52     pos = 0;
53     for (int i = 0; i < maxn; i++) ta[i] = 0;
54 }
55 int main()
56 {
57     for (LL bm, m, tp; scanf("%lld%lld%lld", &p, &b, &n) != EOF; )
58     {
59         init();
60         m = sqrt(p * 1.0); bm = ksm(b, m, p);
61         tp = 1; insert(tp, 0);
62         for (LL i = 1; i <= m; i++)//we must use "<=" here, just consider 8 2 4
63             tp = tp * b % p, insert(tp, i);
64         bm = ksm(ksm(b, m, p), p - 2, p);
65         tp = n % p; ans = 0x7fffffffll + 1ll;
66         for (LL i = 0, f; i <= m; i++)
67         {
68             if (find(tp, f)) ans = min(ans, i * m + f);
69             tp = tp * bm % p;
70         }
71         if (ans <= 0x7fffffffll) printf("%lld
", ans);
72         else puts("no solution");
73         /*m = sqrt(p * 1.0); bm = ksm(b, m, p);
74         tp = 1; insert(tp, 0);
75         for (int i = 1; i <= m; i++)
76             tp = tp * bm % p, insert(tp, i);
77         bm = ksm(b, p-2, p);
78         tp = n * b % p;
79         ans = 0x7fffffffll + 1ll;
80         for (LL i = 0, f; i <= m; i++)
81         {
82             tp = tp * bm % p;
83             if (find(tp, f)) ans = min(ans, f * m + i);
84         }
85         if (ans <= 0x7fffffffll) printf("%lld
", ans);
86         else puts("no solution");*/
87     }
88     return 0;
89 }
90 //when p==b, we can't get b^(-1)
//zhetiqishijiushibaoliyouhua

poj 1284: 求原根个数

  简单题。先求出2~65535里所有的原根,然后输入p,输出φ(p-1)就行了。

  个数定理:设d|(p-1), 则模p的一个既约剩余系中对模p的指数等于d的元素个数等于φ(d).

  另:模m有原根的充要条件是m=1, 2, 4, p^a, 2p^a. 其中,p为奇素数。

 1 //13443437    ooyyloo    1284    Accepted    692K    16MS    G++    643B    2014-09-15 15:43:18
 2 #include <cstdio>
 3 using namespace std;
 4 const int maxn = 65537;
 5 
 6 int euler[maxn], plist[maxn], num;
 7 void doeuler(int n = maxn - 1)
 8 {
 9     for (int i = 2; i <= n; i++)
10     {
11         if (!euler[i]) euler[i] = i - 1, plist[++num] = i;
12         for (int j = 1; j <= num && i * plist[j] <= n; j++)
13         {
14             if (i % plist[j] == 0)
15             {
16                 euler[i * plist[j]] = euler[i] * plist[j];
17                 break;
18             }
19             euler[i * plist[j]] = euler[i] * (plist[j] - 1);
20         }
21     }
22 }
23 
24 int main()
25 {
26     doeuler();
27     for (int p; scanf("%d", &p) != EOF; ) printf("%d
", euler[p - 1]);
28     return 0;
29 }
//primitive roots

poj 3696: 原根的应用

  需要先公式推导出10^m % (9*L/gcd(8,9L)) == 1 这个结论。然后用欧拉函数和原根相关的定理求出最小的m。

  要注意(9*L)^2会超long long,所以此题快速幂里的乘法应该用快速加来实现。

  1 //13445399    ooyyloo    3696    Accepted    704K    360MS    G++    1897B    2014-09-16 00:58:50
  2 #include <cstdio>
  3 #include <algorithm>
  4 using namespace std;
  5 typedef long long LL;
  6 const int maxn = 150001;
  7 
  8 LL gcd(LL a, LL b)
  9 {
 10     LL temp;
 11     while (b != 0) temp = b, b = a % b, a = temp;
 12     return a;
 13 }
 14 LL mul(LL a, LL n, LL p)
 15 {
 16     LL ret = 0;
 17     for (a %= p; n; n >>= 1)
 18     {
 19         if (n & 1) ret = (ret + a) % p;
 20         a = (a + a) %p;
 21     }
 22     return ret;
 23 }
 24 LL ksm(LL a, LL n, LL p)
 25 {
 26     LL ret = 1;
 27     for (a %= p; n; n >>= 1)
 28     {
 29         if (n & 1) ret = mul(ret, a, p);
 30         a = mul(a, a, p);
 31     }
 32     return ret;
 33 }
 34 
 35 bool euler0[maxn];
 36 LL plist[maxn], num;
 37 void doeuler(int n = maxn - 1)
 38 {
 39     for (int i = 2; i <= n; i++)
 40     {
 41         if (!euler0[i]) euler0[i] = 1, plist[++num] = i;
 42         for (int j = 1; j <= num && i * plist[j] <= n; j++)
 43         {
 44             euler0[i * plist[j]] = 1;
 45             if (i % plist[j] == 0) break;
 46         }
 47     }
 48 }
 49 
 50 LL zs[maxn], gs[maxn], cnt;
 51 LL ys[maxn], po;
 52 void div(LL d)
 53 {
 54     cnt = 0;
 55     for (int i = 1; i <= num; i++)
 56         if (d % plist[i] == 0)
 57         {
 58             zs[++cnt] = plist[i]; gs[cnt] = 0;
 59             while (d % plist[i] == 0) gs[cnt]++, d /= plist[i];
 60         }
 61     if (d > 1) zs[++cnt] = d, gs[cnt] = 1;
 62 }
 63 LL euler(LL d)
 64 {
 65     div(d);
 66     LL ret = 1;
 67     for (int i = 1; i <= cnt; i++)
 68     {
 69         ret *= (zs[i] - 1);
 70         for (int j = 2; j <= gs[i]; j++) ret *= zs[i];
 71     }
 72     return ret;
 73 }
 74 void dfs(int s, LL now)
 75 {
 76     if (s == cnt) return;
 77     s++;
 78     dfs(s, now);
 79     for (int i = 1; i <= gs[s]; i++)
 80     {
 81         now *= zs[s];
 82         ys[++po] = now;
 83         dfs(s, now);
 84     }
 85 }
 86 
 87 LL L;
 88 int main()
 89 {
 90     doeuler();
 91     for (int tt = 1; scanf("%lld", &L) != EOF; tt++)
 92     {
 93         if (!L) break;
 94         LL tp = 9 * L / gcd(8, 9 * L);
 95         div(euler(tp));
 96         ys[po = 1] = 1; dfs(0, 1);
 97         sort(ys + 1, ys + 1 + po);
 98         printf("Case %d: ", tt);
 99         bool flag = 0;
100         for (int i = 1; i <= po; i++)
101             if (ksm(10, ys[i], tp) == 1)
102             {
103                 printf("%lld
", ys[i]);
104                 flag = 1;
105                 break;
106             }
107         if (!flag) puts("0");
108     }
109     return 0;
110 }
//dai ma hao chang...

hdu 4992: 求模n的所有原根

   首先,我们需要知道φ(n)的求法。我用的是筛法,把φ(n)用O(n)的时间预处理出来了。理论上直接用φ(n)=n*(1-1/p1)*……*(1-1/pm)也是可以的。

  然后,需要知道原根存在定理。只有n=1, 2, 4, p^a, 2*p^a 的时候有原根。加上这个判断会快很多。

  接着,就是正式的求解过程:

  1. 求最小原根。因为最小原根一般都很小(不信自己输出来看),所以可以直接枚举求解。原根的判定应该用分解φ(n)求约数的方法做,否则速度会很慢。

  2. 根据定理,若a为对模n的最小原根,那么a^x%n也是对模n的最小原根,其中(x,n)=1. 我们现在已经知道a,那么直接求解即可。

  3. 排序输出答案。

  第一步中原根判定也能用gφ(n)/pi != 1 (mod n) , pi | φ(n) 来做,而且据说这样做会快一些。经过试验,确实可行,从理论上来说,效率确实快一些,代码也少一些...

  以上说明若有不懂之处,请找本数论书看看...

  (某本书上说用随机法找原根的效率是多项式...有兴趣的尝试之后请联系窝...

  1 //11663376    2014-09-16 11:06:55    Accepted    4992    1015MS    6416K    2657 B    G++    TofE
  2 #include <cstdio>
  3 #include <algorithm>
  4 using namespace std;
  5 typedef long long LL;
  6 const int maxn = 1000001, maxx = 10000;
  7 
  8 int gcd(int a, int b)
  9 {
 10     int temp;
 11     while (b != 0) temp = b, b = a % b, a = temp;
 12     return a;
 13 }
 14 int pow(int a, int n, int p)
 15 {
 16     int ret = 1;
 17     for (a %= p; n; n >>= 1)
 18     {
 19         if (n & 1) ret = (LL)ret * a % p;
 20         a = (LL)a * a % p;
 21     }
 22     return ret;
 23 }
 24 
 25 int euler[maxn], plist[maxn], num;
 26 void doeuler(int n = maxn - 1)
 27 {
 28     euler[1] = 1;
 29     for (int i = 2; i <= n; i++)
 30     {
 31         if (!euler[i]) euler[i] = i - 1, plist[++num] = i;
 32         for (int j = 1; j <= num && i * plist[j] <= n; j++)
 33         {
 34             if (i % plist[j] == 0)
 35             {
 36                 euler[i * plist[j]] = euler[i] * plist[j];
 37                 break;
 38             }
 39             euler[i * plist[j]] = euler[i] * (plist[j] - 1);
 40         }
 41     }
 42 }
 43 
 44 int zs[maxx], gs[maxx], cnt;
 45 int ys[maxx], yct;
 46 void div(int d)
 47 {
 48     cnt = 0;
 49     for (int i = 1; i <= num; i++)
 50         if (d % plist[i] == 0)
 51         {
 52             zs[++cnt] = plist[i]; gs[cnt] = 0;
 53             while (d % plist[i] == 0) gs[cnt]++, d /= plist[i];
 54         }
 55     if (d > 1) zs[++cnt] = d, gs[cnt] = 1;
 56 }
 57 void dfs(int s, int now)
 58 {
 59     if (s == cnt) return;
 60     s++;
 61     dfs(s, now);
 62     for (int i = 1; i <= gs[s]; i++)
 63     {
 64         now *= zs[s];
 65         ys[++yct] = now;
 66         dfs(s, now);
 67     }
 68 }
 69 
 70 int n;
 71 int ans[maxn], po;
 72 bool YES(int a)
 73 {
 74     for (int i = 1; i <= yct; i++)
 75         if (pow(a, ys[i], n) == 1) return 0;
 76     return pow(a, euler[n], n) == 1;
 77 }
 78 bool rich(int n)
 79 {
 80     if (n == 1 || n == 2 || n == 4) return 1;
 81     div(n);
 82     if (cnt > 2) return 0;
 83     if (cnt == 2 && zs[1] != 2) return 0;
 84     if (zs[1] == 2 && gs[1] > 1) return 0;
 85     return 1;
 86 }
 87 int main()
 88 {
 89     doeuler();
 90     for (int a; scanf("%d", &n) != EOF; )
 91     {
 92         if (n == 1) { puts("1"); continue; }//ke yi bu xie
 93         if (!rich(n))
 94         {
 95             puts("-1");
 96             continue;
 97         }
 98         div(euler[n]);
 99         ys[yct = 1] = 1; dfs(0, 1); yct--;
100         for (int i = 1; i <= n; i++)//--
101             if (YES(i)) { a = i; break; }
102         po = 0;
103         for (int i = 1, tp = a; i <= euler[n]; i++, tp = tp * a % n)// Is  i == 0  possible ?
104             if (gcd(i, euler[n]) == 1) ans[++po] = tp;
105         sort(ans + 1, ans + 1 + po);
106         for (int i = 1, T = 0; i <= po; i++) printf("%s%d", T?" ":"", ans[i]), T = 1;
107         puts("");
108     }
109     return 0;
110 }
//shulundaimadouhaochang...

sgu 261: 求 x^K = A (mod P) 的解

  这道题是一道综合题。要用n多定理及模板。

  解题思路:设r是对模P的原根,那么r^0, r^1, …… , r^(φ(P)-1)构成模P的既约剩余系。而P又是质数,所以它们就够成了模P的剩余系(除了0)。由上述定理知,可设r^j = A (mod P), (r^i)^K = x^K (mod P), 即 r^iK = r^j (mod P), 所以得到i*K = j (mod P-1),然后解线性方程组求出i.

  解题步骤:

  1. 求出模P的最小原根r,再求出r^j = A (mod P).

  2. 求出满足i*K = j (mod P-1) 的最小的i.

  3. 枚举所有可能的i,求出对应的x,排序,输出。

  ps. 注意到当A=0时,r^j = A (mod P)无解,所以这里要特判一下。

  (我在写的时候,一开始枚举i的时候是for到P-1为止,WA了一次。后来发现,当最小的i为0时,i=P-1的答案和i=0的答案是重合的...改了就AC了)

  1 //1593026    16.09.14 18:02    TofE    261     .CPP    Accepted     93 ms    2022 kb
  2 #include <cstdio>
  3 #include <algorithm>
  4 #include <cmath>
  5 #include <map>
  6 using namespace std;
  7 typedef long long LL;
  8 const int maxn = 40001;
  9 
 10 int gcd(int a, int b)
 11 {
 12     int temp;
 13     while (b != 0) temp = b, b = a % b, a = temp;
 14     return a;
 15 }
 16 void _gcd(LL a, LL b, LL &x, LL &y)
 17 {
 18     if (b == 0) { x = 1, y = 0; return; }
 19     _gcd(b, a % b, x, y);
 20     LL temp = y;
 21     y = x - a / b * y;
 22     x = temp;
 23 }
 24 int pow(int a, int n, int p)
 25 {
 26     int ret = 1;
 27     for (a %= p; n; n >>= 1)
 28     {
 29         if (n & 1) ret = (LL)ret * a % p;
 30         a = (LL)a * a % p;
 31     }
 32     return ret;
 33 }
 34 
 35 int euler[maxn], plist[maxn], num;
 36 void doeuler(int n = maxn - 1)
 37 {
 38     for (int i = 2; i <= n; i++)
 39     {
 40         if (!euler[i]) euler[i] = i - 1, plist[++num] = i;
 41         for (int j = 1; j <= num && i * plist[j] <= n; j++)
 42             if (i % plist[j] != 0) euler[i * plist[j]] = euler[i] * (plist[j] - 1);
 43             else j = num + 1, euler[i * plist[j]] = euler[i] * plist[j];
 44     }
 45 }
 46 
 47 int zs[maxn], gs[maxn], cnt;
 48 void div(int d)
 49 {
 50     cnt = 0;
 51     for (int i = 1; i <= num; i++)
 52         if (d % plist[i] == 0)
 53         {
 54             zs[++cnt] = plist[i]; gs[cnt] = 0;
 55             while (d % plist[i] == 0) gs[cnt]++, d /= plist[i];
 56         }
 57     if (d > 1) zs[++cnt] = d, gs[cnt] = 1;
 58 }
 59 
 60 int P, K, A;
 61 int ans[maxn], act; //how many answers?
 62 
 63 bool YES(int d)
 64 {
 65     for (int i = 1; i <= cnt; i++)
 66         if (pow(d, (P - 1) / zs[i], P) == 1) return 0;
 67     return 1;
 68 }
 69 int proot()
 70 {
 71     div(P - 1);
 72     for (int i = 1; i <= P; i++)
 73         if (YES(i)) return i;
 74 }
 75 int dlog(int r)
 76 {
 77     map<int, int> hashf; map<int, int>::iterator it;
 78     int m = sqrt(P * 1.0), tp = 1;
 79     for (int i = 0; i <= m; i++)
 80     {
 81         if (!hashf[tp]) hashf[tp] = i;
 82         tp = (LL)tp * r % P;
 83     }
 84     tp = A;
 85     int rm = pow(pow(r, m, P), P - 2, P);
 86     for (int i = 0; i < m; i++)
 87     {
 88         if ((it = hashf.find(tp)) != hashf.end())
 89             return i * m + it->second; // only exist one answer.
 90         tp = (LL)tp * rm % P;
 91     }
 92 }
 93 void __gcd(int r, int j)
 94 {
 95     if (j % gcd(K, P - 1) != 0) return;
 96     LL x, y, gbs = gcd(K, P - 1), a = K / gbs, b = (P - 1) / gbs;
 97     _gcd(a, b, x, y);
 98     x = (x * ((LL)j / gbs) % b + b) % b;
 99     for (; x < P - 1; x += b) //--
100         ans[++act] = pow(r, x, P);
101 }
102 int main()
103 {
104     doeuler();
105     scanf("%d%d%d", &P, &K, &A);
106     if (A == 0) ans[++act] = 0;
107     else
108     {
109         int r = proot();
110         int j =  dlog(r);
111         __gcd(r, j);
112     }
113     sort(ans + 1, ans + 1 + act);
114     printf("%d
", act);
115     for (int i = 1; i <= act; i++) printf("%d
", ans[i]);
116     return 0;
117 }
//much simpler task...
原文地址:https://www.cnblogs.com/monmonde/p/3971208.html