【ybt金牌导航8-4-4】【luogu P2155】互质个数 / 沙拉公主的困惑(gcd)(欧拉函数)
互质个数 / 沙拉公主的困惑
题目链接:ybt金牌导航8-4-4 / luogu P2155
题目大意
要你求 sum i=1~n![gcd(i,m!)=1]%r。
其中 r 是质数,n>=m。
很多组询问。
思路
重新写式子:
\(\sum\limits_{i=1}^{n!}[gcd(i,m!)=1]\pmod r\)
我们不妨从 \(n>m\) 这个方面下手:
那首先 \(1\sim m!\) 的部分肯定是 \(\varphi(m!)\)。
那超出的部分呢?
我们再考虑一下会发现 \(m!|n!\)。
然后再根据 \(\gcd(a+b,b)=\gcd(a,b)\) 这个东西。
你就会发现超出的部分就是不停的像 \(1\sim m!\) 的部分循环下去,然后因为是 \(m!|n!\),所以最后一次一定是循环完了的!
所以就是循环了 \(\dfrac{n!}{m!}\) 次,那答案就是 \(\dfrac{n!}{m!}\varphi(m!)\)。
然后直接求好像不太行,因为它是很多组询问,所以我们考虑把 \(\varphi\) 用欧拉筛求的原理分解出来。
然后就是:\(\dfrac{n!}{m!}\varphi(m!)=\dfrac{n!}{m!}(m!\prod\dfrac{p_i-1}{p_i})=n!\prod\dfrac{p_i-1}{p_i}\)
(\(p_i\) 是 \(m!\) 质因数分解中的每个质数,其实就相当于 \(1\sim m\) 中的质数)
然后就可以直接搞了(用欧拉筛求质数的时候可以把 \(m\) 取每个值时的 \(\prod\dfrac{p_i-1}{p_i}\) 算出来)
吗?
其实会有一个问题,就是 \(n,m\) 时可能 \(\geqslant r\) 的。
那这个时候不一定是 \(0\),因为可能 \(n!\) 会与 \(\prod\) 中的下面的 \(p_i\) 消掉。
所以准确来说无解的情况时 \(n\geqslant r\) 时 \(m<r\) 或者 \(n\geqslant 2r\)(因为 \(\prod\) 下面至多只会出现一次)。
那如果 \(\geqslant r\) 了但是答案不是 \(0\),那我们计算的时候就直接把 \(r\) 的位置略过去。
(就是 \(n!\) 中和 \(\prod\) 中的下面部分,上面还是要留着 \(r-1\))
然后就可以了。
代码
#include<cstdio>
#define ll long long
using namespace std;
ll t, mo, n, m, jc[10000001], inv[10000001];
ll prime[1000001];
bool np[10000001];
ll ksm(ll x, ll y) {
ll re = 1;
while (y) {
if (y & 1) re = re * x % mo;
x = x * x % mo;
y >>= 1;
}
return re;
}
int main() {
scanf("%lld %lld", &t, &mo);
jc[0] = 1;
for (int i = 1; i <= 10000000; i++)
if (i != mo) jc[i] = jc[i - 1] * i % mo;
else jc[i] = jc[i - 1];//忽略掉 mo
inv[0] = inv[1] = 1;
for (int i = 2; i <= 10000000; i++) {
if (!np[i]) {
if (i != mo) inv[i] = inv[i - 1] * ksm(i, mo - 2) % mo * (i - 1) % mo;
else inv[i] = inv[i - 1] * (i - 1) % mo;//下面忽略掉,上面还要
prime[++prime[0]] = i;
}
else inv[i] = inv[i - 1];
for (int j = 1; j <= prime[0] && i * prime[j] <= 10000000; j++) {
np[i * prime[j]] = 1;
if (i % prime[j] == 0) break;
}
}
while (t--) {
scanf("%d %d", &n, &m);
if (n >= mo) {
if (m < mo || n >= mo * 2) {//直接判无解情况
printf("0\n");
continue;
}
}
printf("%lld\n", jc[n] * inv[m] % mo);
}
return 0;
}