专栏文章
题解:P2155 [SDOI2008] 沙拉公主的困惑
P2155题解参与者 5已保存评论 4
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 4 条
- 当前快照
- 1 份
- 快照标识符
- @miowi257
- 此快照首次捕获于
- 2025/12/03 02:17 3 个月前
- 此快照最后确认于
- 2025/12/03 02:17 3 个月前
题目大意
题意为求 以内与 互质的数的个数()。
思路
我们可以发现一个神奇的性质,由于 ,那么 。然后 内与 互质的数的个数为 ,然后 又为 的倍数,这时候就能猜一猜 内与 互质的数的个数为 ,然后再严谨证明。
推论 :若 ,则 至 与 互质的正整数个数为 。
证明:
先简化,我们知道 至 内与 互质的数的个数为 。
假设 与 互质,根据 ,将 带入 , 带入 ,即 。
此时 ,即 ,即 与 互质,即 加上任意 的倍数均与 互质。
再推广,那么 至 中共有多少个 呢?
由于 ,即 ,由于 为整数,则共有 个 。
由于 ,那么 为整数且 ,。
所以对于每个 ,在 到 中共有 个 。
又由于共有 个 ,则 至 内共有 个数与 互质。
你可能会说除了 形式的数,可能还有其他的数与 互质,我们再来证明一下这不可能成立:
若一个数 与 互质,那么 。
根据欧几里得算法,可得 。
这时 一定为其中一个 ,否则 。
这就说明一个与 互质的数一定可以被写成 的形式。
证毕。
回到题目,此时答案即为 ,然后我们就可以直接做了,预处理 及其逆元,以及 。
那么怎么得到 呢?考虑从 递推过来。
注意到 有一个性质,就是其为所有不大于 的质数的倍数。也就是说 可以写成:
其中所有的 ,分类讨论一下:
当 时,。
当 时,。
于是可以递推得到 。
细节
预处理后,除法用逆元处理即可,提交以后,恭喜你,炸了。
因为题目中并没有说明 ,所以逆元可能会爆炸!
所以,在预处理时,我们要将所有数写成 的形式,用两个数组分别存储 和 。
当两数相乘时将 相乘,将 相加,当两数相除时用除数的 乘上倍数数 的逆元,将 相减即可。
最后,如果答案的 那么答案 的结果即为 ,输出 即可。
反之,如果答案的 ,则输出答案的 即可。
代码
CPP#include<bits/stdc++.h>
using namespace std;
#define ll long long
int qpow(int a, int b, int mod){ // 快速幂
int ans = 1, t = a;
for(; b > 0; b >>= 1){
if(b & 1) ans = ((ll)ans * t) % mod;
t = ((ll)t * t) % mod;
}
return ans;
}
const int N = 10000050;
int t, r, n, m, u, k;
int phia[N], phib[N]; // phia(i)的a和b
int tmsa[N], tmsb[N]; // i!的a和b
bool vis[N];
int main(){
cin >> t >> r;
vis[1] = 1;
// 埃氏筛筛质数
for(int i = 1; i < N; i ++)
if(!vis[i])
for(int j = i; (ll)j * i < N; j ++)
vis[i * j] = 1;
// 预处理
phia[0] = 1; phib[0] = 0; tmsa[0] = 1; tmsb[0] = 0;
for(int i = 1; i < N; i ++){
// 处理阶乘
u = i, k = 0;
while(u % r == 0) u /= r, k ++; // 将i中所有的r除出来
tmsa[i] = ((ll)tmsa[i - 1] * u) % r; /*将a相乘*/ tmsb[i] = tmsb[i - 1] + k /*将b相加*/;
// 处理phi前缀积
u = (vis[i] ? i : i - 1), k = 0;
while(u % r == 0) u /= r, k ++; // 将i中所有的r除出来
phia[i] = ((ll)phia[i - 1] * u) % r; /*将a相乘*/ phib[i] = phib[i - 1] + k /*将b相加*/;
}
while(t --){
scanf("%d%d", &n, &m);
int ans = (ll)tmsa[n] * phia[m] % r * qpow(tmsa[m], r - 2, r) % r; // 求出答案的a
if(tmsb[n] + phib[m] - tmsb[m] == 0) printf("%d\n", ans); // 若答案的b为零,输出ans的a
else printf("0\n"); // 否则答案为r的倍数,输出0
}
return 0;
}
相关推荐
评论
共 4 条评论,欢迎与作者交流。
正在加载评论...