题意:给出n,求phi(n)
温习一下太久没用过的板子。
miller_rabin:基于费马小定理a^p-1==1 mod p
和二次探测定理 a^2==1 mod p <==> a==1 or a==p-1 mod p
单次测试的正确率约为75%。
pollard_pho:设置两个初值相同的变量,每次平方后加上一个常数,一个变量一次走一步,另一个一次走两步,若gcd(p,abs(a-b))!=1则找到一个因子,然后递归两边。如果出现环了还没找到则更换初值和常数继续找。
时间复杂度四次根号p。
注意miller_rabin和pollard_pho里的各种特判。
n==1需要特判掉。
#include<cstdio>
#include<tr1/random>
#include<ext/pb_ds/assoc_container.hpp>
#ifdef ONLINE_JUDGE
#define null_type __gnu_pbds::null_mapped_type
#else
#define null_type __gnu_pbds::null_type
#endif
using namespace std;
typedef long long ll;
inline ll mul(const ll &a,const ll &b,const ll &m)
{
return ((a*b-(ll)((long double)a/m*b)*m)+m)%m;
}
inline ll qpow(ll a,ll x,const ll &m)
{
ll ans=1;
while(x)
{
if(x&1) ans=mul(ans,a,m);
a=mul(a,a,m);
x>>=1;
}
return ans;
}
inline bool miller_rabin(ll a,ll u,int c,ll p)
{
a=qpow(a,u,p);
while(c--)
{
ll b=mul(a,a,p);
if(b==1&&(a!=1&&a!=p-1)) return 0;
a=b;
}
return a==1;
}
inline bool is_prime(const ll &p)
{
if(p==2||p==3||p==5) return 1;
if(p<2||!(p&1)) return 0;
ll u=p-1;
int c=0;
while(!(u&1)) u>>=1,++c;
return miller_rabin(2,u,c,p)&&miller_rabin(3,u,c,p)&&miller_rabin(5,u,c,p);
}
inline ll gcd(ll a,ll b)
{
ll c;
while(b)
c=a,a=b,b=c%b;
return a;
}
inline ll pollard_pho(ll a,ll c,ll p)
{
if(!(p&1)) return 2;
ll b=(mul(a,a,p)+c)%p;
ll res=gcd(p,a<b?b-a:a-b);
while(res==1)
{
a=(mul(a,a,p)+c)%p;
b=(mul(b,b,p)+c)%p,b=(mul(b,b,p)+c)%p;
res=gcd(p,a<b?b-a:a-b);
}
return res;
}
__gnu_pbds::cc_hash_table<ll,null_type> t;
tr1::mersenne_twister<unsigned long long,64,312,156,31,0xb5026f5aa96619e9ULL,29,17,0x71d67fffeda60000ULL,37,0xfff7eee000000000ULL,43> en(114514);
ll stk[50];
int tot=0;
void decompose(ll x)
{
if(t.find(x)!=t.end()) return;
t.insert(x);
if(is_prime(x))
{
stk[++tot]=x;
return ;
}
ll y=x;
tr1::uniform_int<long long> rnd(1,x-1);
while(x==y)
y=pollard_pho(rnd(en),1,x);
decompose(y);decompose(x/y);
}
ll n;
int main()
{
scanf("%lld",&n);
if(n==1)
{
puts("1");
return 0;
}
decompose(n);
for(int i=1;i<=tot;++i)
n-=n/stk[i];
printf("%lld\n",n);
return 0;
}