题目连接:链接: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<bits/stdc++.h>
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;
}
若有什么错误,欢迎指正^ _ ^ 。