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;
    }



  • 相关阅读:
    hdu 5366 简单递推
    hdu 5365 判断正方形
    hdu 3635 并查集
    hdu 4497 数论
    hdu5419 Victor and Toys
    hdu5426 Rikka with Game
    poj2074 Line of Sight
    hdu5425 Rikka with Tree II
    hdu5424 Rikka with Graph II
    poj1009 Edge Detection
  • 原文地址:https://www.cnblogs.com/staginner/p/2364084.html
Copyright © 2011-2022 走看看