题解
直接暴力将m和区间[1, n]进行求gcd判断会超时,考虑m和[1, n]是否互质可以转换为m的任意一个质因子知否能整除[1, n]。
暴力法:
区间[1, n]每个数字尝试除以m的每个质因子如果都无法整除则说明与m互质累乘至答案,复杂度O(Tnlogm)。(数据水了非正解)
容斥法:
答案为 n! / 每个质因子的倍数 * 两两质因子乘积的倍数 / 三个质因子乘积的倍数… 所有质因子乘积的倍数。
将[1, n]每个的阶乘都打表求出,求出m的质因子。质因子乘积p的倍数累积=π(q*p),q为[1, n/p],可以化为q! * p^q。
二进制枚举每个状态求出p,记录二进制位数量k偶数乘奇数除(乘上逆元),复杂度O(logn*2^logm)。
AC代码
暴力法:
#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
const int MOD = 1e9 + 7;
bool vis[N];
int main()
{
#ifdef LOCAL
//freopen("C:/input.txt", "r", stdin);
#endif
int n, m;
while (scanf("%d%d", &n, &m) != EOF)
{
ll res = 1;
if (m == 1)
for (ll i = 1; i <= n; ++i)
res = res * i % MOD;
else
{
memset(vis, 0, sizeof(vis));
for (ll i = 2; i * i <= m; ++i) //分解素因子
if (m % i == 0)
{
for (ll j = 1; i * j <= n; ++j) //标记素因子的倍数
vis[i * j] = 1;
while (m % i == 0)
m /= i;
}
if (m > 1)
for (ll j = 1; m * j <= n; ++j)
vis[m * j] = 1;
for (ll i = 1; i <= n; ++i)
if (!vis[i])
res = res * i % MOD;
}
printf("%lld\n", res);
}
return 0;
}
容斥法:
#include <stdio.h>
#include <bits/stdc++.h>
#define fst first
#define sed second
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const int MOD = 1e9 + 7;
const int N = 1e6 + 10;
ll fac[N];
ll qpow(ll a, ll b)
{
ll res = 1;
while (b)
{
if (b & 1)
res = res * a % MOD;
a = a * a % MOD;
b >>= 1;
}
return res;
}
int main()
{
#ifdef LOCAL
//freopen("C:/input.txt", "r", stdin);
#endif
fac[0] = 1;
for (int i = 1; i < N; ++i)
fac[i] = fac[i - 1] * i % MOD;
int n, m;
while (scanf("%d%d", &n, &m) != EOF)
{
vector<int> pri;
int t = m;
for (int i = 2; i * i <= t; ++i) //分解质因子
if (t % i == 0)
{
pri.push_back(i);
while (t % i == 0)
t /= i;
}
if (t > 1)
pri.push_back(t);
ll ans = 1;
for (int i = 0; i < (1 << pri.size()); ++i) //枚举集合
{
ll p = 1, q, k = 0; //当前状态乘积 二进制位数
for (int j = 0; j < pri.size(); ++j)
if (i & (1 << j))
p = 1LL * p * pri[j] % MOD, ++k; //当前状态累乘进p
q = n / p; //p倍数的数量
ll res = fac[q] * qpow(p, q) % MOD; //p倍数累乘q! * p^q
ans = ans * (k & 1 ? qpow(res, MOD - 2) : res) % MOD; //容斥 偶数乘 奇数除
}
printf("%lld\n", ans);
}
return 0;
}