poj3696

欧拉定理,对于正整数a,n,若gcd(a,n)=1,则有a^euler(n)=1(mod n)。

快速幂取模算法,只要p*2在long long范围内都可以计算,对于算法中long long * long long的情况可能超界,但是我们可以用一种类似于快速幂的方法来进行次乘法计算,即快速幂是用乘法代替幂计算以便及时取模,而此算法则是用加法代替乘法计算及时取模,把快速幂中的乘法换成加法,平方换成乘2即可。

题意:给出数字L,问一个各位数字都是8的L的倍数是否存在,若存在,输出其最小长度。

分析:欧拉定理,我们可以列出方程(10^x - 1) / 9 = L * k / 8,推出(10^x - 1) * 8 = k * 9L,我们现在想要把它化成10^x=1(mod p)的形式,因此我们要看能从这个等式中找到10^x-1是谁的倍数,且要保证p中一定要包含L,且不包含k。我们需要这样做,让等式变为(10^x-1)*a=k*b的形式,且b中要包含L且不包含k,要求gcd(a,b)=1。如果满足以上条件我们就可以证明(10^x-1)是b的倍数了,因为要使等式成立,组成b的因子在等式左边,没有包含在a中,则必然包含在(10^x-1)中。现在若gcd(8,9L)=1则等式符合要求了,但是无法证明它等于1,所以我们将等式改写(10^x-1)*(8/gcd(8,9L))=k*(9L/gcd(8,9L)),这回a,b就互质了。所以我们让p=9L/gcd(8,9L)。

然后我们就有10^x-1=0(mod p),即10^x=1(mod p)。这样就符合了欧拉定理的形式。

现在若gcd(10, p)=1则x必然有解,因为x=phi(p)即可。但我们现在要求的是最小正整数解,mod p乘法是有循环节的,即设循环节为r,那么10^x==10^(x%r)。x=0的时候10^0=1(mod p),有因为在一个循环中乘法结果是不会有重复数字的,即对于xi != xj且xi < r, xj < r,则有10^xi != 10^xj。因此要使得等式10^x=1(mod p)成立,必须有x%r=0。因此最小正整数解就是第一个循环节。那么既然phi(p)是解说明它在循环节上,是循环节的倍数,因此只要枚举phi(p)的所有约数其中一定有循环节,找到最小的可以使得等式成立的即为所求。

对于gcd(10, p) != 1的情况,方程一定无解。设gcd(10,p)=g,因为10^x % g = 0,可以看作10^x=g*y。设p=g*z,10^x %p = (g*y)%(g*z) = g*y-n*(g*z) = g*(y-n*z) 所以一定是g的倍数,不会等于1。

综上,我们代码处理过程为,先求phi(p),p=9L/gcd(8,9L)。然后用跟好phi(p)的效率求其所有约数,对于每个约数,用快速幂验证是否满足方程10^x=1(mod p)。找到满足方程的最小的一个。

View Code
#include <iostream>

#include <algorithm>

#include <cstdlib>

#include <cstdio>

#include <cstring>

using namespace std;



#define maxn (1 << 16)



long long n;

long long factor[maxn];



long long euler(long long n)

{

    long long ret = n;

    for (long long i = 2; i * i <= n; i++)

        if (n % i == 0)

        {

            ret = ret / i * (i - 1);

            while (n % i == 0)

                n /= i;

        }

    if (n > 1)

        ret = ret / n * (n - 1);

    return ret;

}



long long mult(long long a, long long b, long long p)

{

    long long ret = 0;

    while (b)

    {

        if (b & 1)

            ret = (ret + a) % p;

        a = 2 * a % p;

        b >>= 1;

    }

    return ret;

}



long long power(long long x, long long n, long long p)

{

    long long ret = 1;

    x %= p;

    while (n)

    {

        if (n & 1)

            ret = mult(ret, x, p);

        x = mult(x, x, p);

        n >>= 1;

    }

    return ret;

}



long long work()

{

    n *= 9;

    for (int i = 0; i < 3; i++)

        if (n % 2 == 0)

            n /= 2;

        else

            break;

    long long p = n;

    if (p % 2 == 0 || p % 5 == 0)

        return 0;

    long long phi = euler(p);

    int cnt = 0;

    for (long long i = 1; i * i <= phi; i++)

        if (phi % i == 0)

        {

            factor[cnt++] = i;

            factor[cnt++] = phi / i;

        }

    sort(factor, factor + cnt);

    for (int i = 0; i < cnt; i++)

        if (power(10, factor[i], p) == 1)

            return factor[i];

    return 0;

}



int main()

{

//    freopen("t.txt", "r", stdin);

    int t = 0;

    while (scanf("%lld", &n), n)

    {

        t++;

        printf("Case %d: ", t);

        printf("%lld\n", work());

    }

    return 0;

}

你可能感兴趣的:(poj)