zoukankan      html  css  js  c++  java
  • 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;
    }
  • 相关阅读:
    GitHub 更新fork的代码
    robotframework出现错误:Keyword 'AppiumLibrary.Open Application' expected 1 to 2 non-keyword arguments,got 5.
    adb命令积累
    appium测试android环境搭建(win7)
    小明的自留地
    转载:appium踩过的坑
    junit3和junit4的使用区别如下
    Python线程指南
    实现ie低版本支持input type="number"
    LODOP打印开发
  • 原文地址:https://www.cnblogs.com/rainydays/p/2754760.html
Copyright © 2011-2022 走看看