TOJ_5474_学习之一个等式(威尔逊定理)

题目连接:链接:http://www.tzcoder.cn/acmhome/problemdetail.do?method=showdetail&id=5474
描述

x! mod (x+1) = x
zzx和city发现了如上的一个等式,想解方程求出x的值,却发现有许多x的值均满足这个等式,随着x的增加zzx发现阶乘太难算,于是叫city写一个大数模拟,而city沉迷于找规律不可自拔。所以现在zzx想请你帮忙求一下对于一个大数x是否满足这个等式。

输入

输入数据有多组,输入到文件结束为止。
每组包括一个正整数x(x<=10^19)。

输出

每次输出占一行,样式为Case i: ans,表示第i组测试数据答案为ans。
ans为yes表示输入的x满足题目的等式,ans为no表示输入的x不满足这个等式。

思路:
一开始没有思路,只感觉应该是一个定理的运用,但我当时并不知道。借助网络了解到了威尔逊定理
(p−1)!≡p−1≡−1 (mod p) (p is a prime),因为p为质数所以(1~p-1)每个数的逆元都在1到p-1内且各不相同,因为a^2%p=1的解在a∈[1,p-1]里只有1和p-1(简单证明一下 :a^2-1≡0(mod p)->(a-1)(a+1)≡0(mod p),只有a=1和p-1时才成立)因此(p-1)!=1*(p-1)*(其他数与其逆元的乘积),对p取模后就成了p-1。因此只要x+1为质数那么x!mod(x+1)=x就成立。
那么有没有可能(x+1)不是质数而出现答案为"yes"的情况呢?这里就简单证明下威尔逊的逆定理(有参考网上的证明)若(x+1)为合数那么它可以写成a * b,因为 a,b∈[2,x]所以在x!中存在着a,所以a整除x!,假设x!≡-1(mod x+1),那么(x+1)整除(x!+1),因此a也整除(x!+1),这显然在a>1的情况下不可能发生。
综上,当且仅当x+1为质数时,答案才为"yes",所以只要判断x+1是否为质数就行;由于x<=1e19所以,需要运用米勒罗宾素数测试来判断。
注意事项:
x的范围>2^63-1所以需要unsigned long long(下用ull代替) 来读入和操作;
个人认为本题最重要的注意事项:运用快速乘法来防只溢出还不够,在做快速乘时加法也会溢出,毕竟2^64-1≈1.84e19,不幸中的万幸,x最大只有1e9,所以ull只会溢出一次还是可以“抢救”的。(这个害我wa到怀疑人生。。。。。。,我说怎么那么多人交java);

#include
using namespace std;
typedef unsigned long long ull ;
ull base;
ull add(ull a,ull b,ull mod)
{
    ull mx=max(a,b);
    if(a+b>=mx)return (a+b)%mod;
    else
    {
        ull ans=base%mod;
        ans=(ans+a+b+1)%mod;
        return ans;
    }
}
ull q_mul(ull a,ull b,ull mod)
{
    ull ans=0;
    while(b)
    {
        if(b&1)ans=add(ans,a,mod);
        a=add(a,a,mod);
        b>>=1;
    }
    return ans;
}
ull q_pow(ull a,ull n,ull mod)
{
    ull ans=1;
    while(n)
    {
        if(n&1)ans=q_mul(ans,a,mod);
        a=q_mul(a,a,mod);
        n>>=1;
    }
    return ans;
}
bool miller(ull x)
{
    if(x==2)return true;
    if(x<2||x%2==0)return false;
    int t=3,r=0;
    ull a,tmp,n=x-1,b;
    srand(101);
    while(!(n&1))n>>=1,r++;
    while(t--)
    {
        a=rand()%(x-1)+1;
        b=q_pow(a,n,x);
        for(int i=0;i<r;i++)
        {
            tmp=q_mul(b,b,x);
            if(tmp==1&&b!=1&&b!=x-1)return false;
            b=tmp;
        }
        if(b!=1)return false;
    }
    return true;
}
int main()
{
    int t=1;
    ull x;
    base--;
    while(cin>>x)
    {
        cout<<"case "<<t++<<':';
        if(miller(x+1))cout<<"yes\n";
        else cout<<"no\n";
    }
    return 0;
}

若有什么错误,欢迎指正^ _ ^ 。

你可能感兴趣的:(数论,看起来很厉害的题,然而~)