莫比乌斯反演是数论中一个著名的内容,可以用来解决很多组合数学方面的问题。
先引入莫比乌斯函数:
有一个定理:
莫比乌斯反演定理的表述为对于f和g两个函数,以下两个式子可以相互推出:
hdu6390 给定n、m、p,求 对p取模的值。
尝试对式子进行化简,首先了解欧拉函数的一个性质,如果a是素数p的k次幂,则
对于i和j的一个因子p,设,则有:
扩展至i和j的所有因数,便有:。控制k使得:,
那么:,原问题变成了求。
稍作变形,所求的问题即为:
令
由莫比乌斯反演有:
则
/*ргргрг*/
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <map>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 1000050;
const int INF = 0x3f3f3f3f;
const double eps = 1e-8;
int t, n, m;
ll mod;
ll eul[maxn], mu[maxn], inv[maxn];
void init()
{
memset(eul, 0, sizeof(eul));
eul[1] = mu[1] = 1;
for(int i = 1;i < maxn;i++) eul[i] = i;
for(int i = 2;i < maxn;i++)
{
if(eul[i] == i)
for(int j = i;j < maxn;j += i)
eul[j] = eul[j]/i*(i - 1);
}
for(int i = 1;i < maxn;i++)
{
for(int j = 2*i; j < maxn;j += i)
mu[j] -= mu[i];
}
}
ll g(int s, int e)
{
ll res = 0;
for(int i = 1;i <= min(s, e);i++)
{
res += 1LL*mu[i]*(s/i)*(e/i);
res %= mod;
}
return res;
}
int main()
{
scanf("%d", &t);
init();
while(t--)
{
scanf("%d%d%lld", &n, &m, &mod);
inv[1] = 1;
for(int i = 2;i <= min(n, m);i++)
inv[i] = inv[mod % i]*(mod - mod/i) % mod;
ll ans = 0;
for(int i = 1; i <= min(n, m);i++)
{
ans += 1LL*g(n/i, m/i)*(1LL*i*inv[eul[i]]) % mod;
ans %= mod;
}
printf("%lld\n", ans % mod);
}
return 0;
}