http://acm.hdu.edu.cn/showproblem.php?pid=1286
题意:求与n互质的数的个数。
首先是尽人皆知的裸欧拉函数:
#include <stdio.h>
#include <algorithm>
using namespace std;
typedef long long ll;
ll euler(ll n)
{
ll ans = n;
for(int i = 2; i*i <= n; i++)
{
if(n%i == 0)
{
ans -= ans/i;
while(n%i == 0)
n /= i;//彻底消除当前素因子
}
}
if(n > 1) ans -= ans/n;
return ans;
}
int main()
{
// freopen("in.txt", "r", stdin);
ll n, ans;
int t;
scanf("%d", &t);
while(t--)
{
scanf("%lld", &n);
printf("%lld\n", euler(n));
}
return 0;
}
我们知道:
欧拉函数是求小于或等于n的数中与n互质的数的数目;
容斥原理是求小于或等于n的数中有多少能被集合m中元素整除。
而对于素数问题,我们把欧拉函数转变一下,用容斥原理的角度思考。
对n分解质因子,我们知道1~(n-1)内是n的质因子的倍数的那些数肯定与n不互质,先求出质因子集合。
问题转变为:
小于或等于n的数中有多少不能被质因子集合m中元素整除;
于是集合m变为质因子集合,求出来的是质因子的倍数数目,也就是合数,减一下就是质数的数目。
#include <stdio.h>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 33005;
int n, cnt, num[N], ans;
void euler(ll n)
{
for(int i = 2; i*i <= n; i++)
{
if(n%i == 0)
{
num[cnt++] = i;
while(n%i == 0)
n /= i;//彻底消除当前素因子
}
}
if(n > 1) num[cnt++] = n;
}
ll gcd(ll a, ll b)
{
if(b == 0) return a;
return gcd(b, a%b);
}
ll lcm(ll a, ll b)
{
return a*b/gcd(a, b);
}
void dfs(ll th, ll now, ll step)
{
if(step > cnt) return;
ll LCM = lcm(now, num[th]);
if(step&1) ans += (n-1)/LCM;
else ans -= (n-1)/LCM;
for(ll i = th+1; i < cnt; i++)
dfs(i, LCM, step+1);
}
int main()
{
// freopen("in.txt", "r", stdin);
int t;
scanf("%d", &t);
while(t--)
{
cnt = 0;
ans = 0;
scanf("%d", &n);
euler(n);
for(int i = 0; i < cnt; i++)
{
dfs(i, num[i], 1);
}
printf("%d\n", (n-1) - ans);
}
return 0;
}