POJ 3358 POJ 3696 (同余方程+欧拉函数+原根)

先说一下原跟的一个基本定理

如果正整数(a,m) = 1和正整数 d 满足a^d≡1(mod m),则 d 整除 φ(m)。
证明如下:
设k不是φ(m)的约数
10^k≡1(mod n)
假设gcd(k,φ(n))=s,必然有一个数a,a是k的倍数,a+s是φ(n)的倍数。
10^a≡10^k≡1(mod n)
10^(a+s)≡10^φ(n)≡1(mod n)
所以10^s≡1(mod n)
而k不是φ(n)的约数,s是φ(n)的约数,s又是k的约数
所以s<k。而若k是符合要求的,则必然有一个更小的s。
所以答案一定是φ(n)的约数。`

POJ 3358

题解
比如1/10,用乘二法,2/10,4/10,8/10,6/10,2/10
其中1*2^1=1*2^5(mod10)
用p代表分子,q代表分母,i代表一个位置,j代表一个位置,比如在1/10中,i为1,j为5,一下的p和q均为最简式
那么我们要求的就是
p*2^i=p*2^j(modq)
化简得2^(j-i)=1(modq)
又有欧拉函数的基本定理可以知道:
a ^ phi(m) ≡ 1 (mod m) (a,m)=1;
又根据原根的基本定理可以知道:
如果正整数(a,m) = 1和正整数 d 满足a^d≡1(mod m),则 d 整除 φ(m)。
所以2跟q互质才能进行下面的求解。
因为 p*2^i=p*2^j(modq)
所以p * 2^i * ( 2^(j-i) - 1 ) ≡ 0 ( mod q )
所以q | p * 2^i * ( 2^(j-i) - 1 )
因为p跟q互质(用乘二法前将分子分母化为最简形式)
所以 q | 2^i * ( 2^(j-i) - 1 )
因为 ( 2^(j-i) - 1 ) 为奇数,同时( q / 2^i )| ( 2^(j-i) - 1 )
所以 令q’=( q / 2^i )
q’就跟2互质(因为都说 ( 2^(j-i) - 1 ) 为奇数了,奇数能被一个数整除,那么这个数总不可能为偶数是吧)
所以i也就是所求的的循环前缀。即q里面有多少个2
求得最小的j-i,所以也就是求得phi(q’)的因子中最小且满足上面方程的解。

ac代码:

#include<cstdio>
#include<algorithm>

#define ll long long
using namespace std;

inline int phi(int n){
    int ans=n;
    for(int i=2;i*i<=n;i++){
        if(n%i==0){
            ans=ans/i*(i-1);
            while(n%i==0) n/=i;
        }
    }
    if(n>1) ans=ans/n*(n-1);
    return ans;
}

int gcd(int a,int b){
    return b==0?a:gcd(b,a%b);
}

inline int quick_mod(int a,int b,int m){
    int ans=1;
    while(b){
        if(b&1) ans=(long long)ans*a*m;
        b>>=1;
        a=(long long)a*a%m;
    }
    return ans;
}

int a[9999999];

int main(void)
{
    int casei=1;
    int p,q;
    char ch;
    while(scanf("%d%c%d",&p,&ch,&q)!=EOF){
        int g=gcd(p,q);
        p=p/g;
        q=q/g;
        int len1=1;
        while(q%2==0){
            len1++;
            q/=2;
        }
        int n=phi(q);
        int k=0;
        for(int i=1;i*i<=n;i++){
            if(n%i==0){
                a[k++]=i;
                a[k++]=n/i;
            }
        }
        sort(a,a+k);
        int len2;
        for(int i=0;i<k;i++){
            if(quick_mod(2,a[i],q)==1){
                len2=a[i];
                break;
            }
        }
        printf("Case #%d: %d,%d\n",casei++,len1,len2);
    }
    return 0;
}

POJ 3696

题解
8*(10x−1)/9=k*L其中k为常数
然后呢因为这个9在分母上,如果涉及取余或者什么东西的话会很麻烦,所以我们把它乘到右边,然后会变成什么呢?
8*(10x−1)=9*k*L
再进一步8*(10x−1)/gcd(8,L)=9*k*L/gcd(8,L)
接下来便于计算
我们令p=8/gcd(8,L),q=9*L/gcd(8,L)
所以原式变为(10x−1)*p=k*q
因为p与q是互质的,这就是为什么我除了个最大公约数。
所以可得10^x≡1(modq)
因为(10x−1)*p%q=0,而且p,q互质,所以(10x−1)%q=0

#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <queue>
#include <vector>
#include<algorithm>

using namespace std;

typedef long long ll;
const int N=1e6;
ll a[999999];
bool book[N];
int prime[N/2];
int len;

void quick_pri(){
    book[0]=book[1]=1;
    for(int i=2;i<N;i++){
        if(!book[i]) prime[len++]=i;
        for(int j=0;j<len&&i*prime[j]<N;j++){
            book[i*prime[j]]=1;
            if(i%prime[j]==0) break;
        }
    }
}

ll gcd(ll a,ll b){
    return b==0?a:gcd(b,a%b);
}

ll phi(ll n){
    ll ans=n;
    for(ll i=0;prime[i]*prime[i]<=n;i++){
        if(n%prime[i]==0) ans=ans/prime[i]*(prime[i]-1);
        while(n%prime[i]==0) n/=prime[i];
    }
    if(n>1) ans=ans/n*(n-1);
    return ans;
}

ll mul(ll a,ll b,ll m){
    ll ans=0;
    while(b){
        if(b&1) ans=(ans+a)%m;
        b>>=1;
        a=(a<<1)%m;
    }
    return ans;
}

ll quick_pow(ll a,ll b,ll m){
    ll ans=1;
    while(b){
        if(b&1) ans=mul(ans,a,m);
        b>>=1;
        a=mul(a,a,m);
    }
    return ans;
}

int main()
{
    quick_pri();
    ll l;
    int casei=1;
    while(scanf("%lld",&l)!=EOF,l){
        ll q;
        q=l/gcd(8,l)*9;
        if(gcd(10,q)!=1) {
            printf("Case %d: 0\n",casei++);
            continue;
        }
        ll t=phi(q);
        ///下面注释掉的三行代码是防止爆longlong,里面蕴藏着我不知道的欧拉定理。。。。+
        ///但这个题不用这么做也可AC
//        if(q%3==0) t*=9;
//        else t*=6;
//        q*=9;
        int k=0;
        for(ll i=1;i*i<=t;i++){
            if(t%i==0){
                a[k++]=i;
                a[k++]=t/i;
            }
        }
        sort(a,a+k);
        int flag=0;
        for(int i=0;i<k;i++){
            if(quick_pow(10,a[i],q)==1){
                flag=1;
                printf("Case %d: %lld\n",casei++,a[i]);
                break;
            }
        }
        if(!flag){
            printf("Case %d: 0\n",casei++);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值