zoukankan      html  css  js  c++  java
  • HNU 10694 Primitive Root

    HNU_10694

        这个题目本质上就是去求模p的原根。

        首先,我们用反证法证明这个结论:如果对于任意的正整数i,r^i%p包含了1~p-1所有整数的话,那么满足r^i%p=1这个等式的最小正整数为p-1。

        如果上面的命题不成立,不妨设满足r^i %p=1这个等式的最小的正整数为j,假如j<p-1,那么对于任意r^i总能分解成若干r^j的与r^x(x<j)的积,所以r^i%p最多有x个不同的值,那么r^i%p不可能包含了1~p-1所有的整数。假如j>p-1,那么就说明当1<=x<=p-1时,不会出现a^x%p的值为1,那么就必然有x1、x2(1<=x1<x2<=p-1)使得r^x1%p=r^x2%p,也就是说r^(x2-x1)%p=1,而这已经在前面证明了是会推出矛盾的。

        因此,如果对于任意的正整数i,r^i%p包含了1~p-1所有整数的话,那么满足r^i%p=1这个等式的最小正整数为p-1。

        接着,我们以上面的结论为前提,即承认满足r^i%p=1这个等式的最小正整数为p-1,然后用反证法证明这个结论:对于任意满足1<=x<y<=p-1的x、y,都有r^x%p !=r^y%p成立。

        假设r^x%p==r^y%p成立的话,那么就会得到r^(y-x)%p=1,这样就与假设相矛盾了。

        因此,对于任意满足1<=x<y<=p-1的x、y,都有r^x%p !=r^y%p成立,也就是说当i从1开始取到p-1时r^i%p就产生了p-1个不同的正整数,进而就可说明对于任意正整数i,r^i%p包含了1~p-1所有整数。

        有了上面两点证明之后,就能够说明“满足r^i%p=1这个等式的最小正整数为p-1”这一命题和“对于任意的正整数i,r^i%p包含了1~p-1所有整数”这个命题是等价的(前面两个证明相当于是必要性和充分性的证明)。

        而由于p是素数,所以欧拉函数是p-1,这就说明了r就是模p的原根。

        兜了这么一个大圈子,就是为了说明题目中描述的r就是模p的原根(虽然题目一开始就是用primitive root去描述r的,但这个描述并不是原根的定义)。

        接下来就是去找模p的原根了,也就是去找满足当i<p时当且仅当i=p-1才有r^i%p=1成立的最小的r。所以,可以暴力枚举r,然后去看当i<p时是否只有i=p-1时才有r^i%p=1成立即可。

        直接暴力固然可以,但是当p比较大的时候耗时就比较长了。一个比较好的优化就是运用一个定理:设p是奇素数,p-1的所有的不同的素数时q1,...,qs,那么,g是模p的原根的充要条件是g^((p-1)/qj)%p !=1,j=1,...,s。由于一个数的素因子数量很少,同时再利用快速幂取模去验证的话就会省不少时间。

    //Program #1
    #include<stdio.h>
    #include<string.h>
    int p;
    int check(int r)
    {
    int i, ans = 1;
    r %= p;
    for(i = 1; i < p - 1; i ++)
    {
    ans = (ans * r) % p;
    if(ans == 1)
    return 0;
    }
    ans = (ans * r) % p;
    return ans == 1;
    }
    void solve()
    {
    int r;
    for(r = 1; ; r ++)
    if(check(r))
    {
    printf("%d\n", r);
    return ;
    }
    }
    int main()
    {
    int t;
    scanf("%d", &t);
    while(t --)
    {
    scanf("%d", &p);
    solve();
    }
    return 0;
    }
    //Program #2
    #include<stdio.h>
    #include<string.h>
    #include<math.h>
    #define MAXD 20010
    int isprime[MAXD], prime[MAXD], p, N;
    void prepare()
    {
    int i, j, k;
    memset(isprime, -1, sizeof(isprime));
    N = 0;
    for(i = 2; i < 20000; i ++)
    if(isprime[i])
    {
    prime[N ++] = i;
    for(j = i * i; j < 20000; j += i)
    isprime[j] = 0;
    }
    prime[N] = 20000;
    }
    int pow_mod(int a, int n)
    {
    int ans;
    if(n == 1)
    return a % p;
    ans = pow_mod(a, n / 2);
    ans = (ans * ans) % p;
    return n % 2 ? (ans * a) % p : ans;
    }
    int check(int r)
    {
    int i, j, k = p - 1;
    for(i = 0; prime[i] < p - 1; i ++)
    if(k % prime[i] == 0 && pow_mod(r, k / prime[i]) == 1)
    return 0;
    return 1;
    }
    void solve()
    {
    int i, j, k;
    for(i = 2; ; i ++)
    if(check(i))
    {
    printf("%d\n", i);
    return ;
    }
    }
    int main()
    {
    int t;
    prepare();
    scanf("%d", &t);
    while(t --)
    {
    scanf("%d", &p);
    if(p == 2)
    printf("1\n");
    else
    solve();
    }
    return 0;
    }



  • 相关阅读:
    STL_string容器
    STL简介
    C++文件输入输出
    Qfile22
    QFile111
    v-model原理
    Scrum敏捷软件开发方法
    大神讲故事:微服务及相关技术,很生动,另附ws和restful区别
    js异步请求方法
    SQL Server中char、varchar、text和nchar、nvarchar、ntext的区别 (转)
  • 原文地址:https://www.cnblogs.com/staginner/p/2364084.html
Copyright © 2011-2022 走看看