专栏文章
UVA12799 题解
UVA12799题解参与者 2已保存评论 1
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 1 条
- 当前快照
- 1 份
- 快照标识符
- @mipudaep
- 此快照首次捕获于
- 2025/12/03 18:05 3 个月前
- 此快照最后确认于
- 2025/12/03 18:05 3 个月前
题意简述
正解思路
这道题的每组数据需要做三件事。
一、求出
按照题意,,不妨 ,又有 ,所以一定有 ,并且 。所以,可以从 到 枚举 (显然,这样的 只有一个)。找到后,求出 ,进而求出 。
时间复杂度:。
时间复杂度:。
二、求出 在模 意义下的逆元
由题意,。那么,已知 与 ,如何求出 呢?
有一种方法,根据欧拉定理,因为 ,所以可以求出 (求 函数的方法就用模板即可,求幂方法同【三、求出 】中的方法)。虽然这种方法的时间复杂度是 ,足以通过此题,但是其实有一种更优的方法(拓展欧拉定理),在这里介绍一下。
首先,因为 ,所以有 。
注意到,若有一方程 ,其中 ,,且 。则令 ,有 ,则解出的 就是 在模 意义下的逆元,即 在模 意义下的逆元。
那么怎么解出这个方程呢?其实最开始的方程的右边是 ,只是因为我们代入的值使其为 。我们想到,可否使用类似于求最大公因数的方法来求解这个方程呢?
若有 ,且 ,则令 ,得到一个新的方程 (因为 )。直到 ,令此时的 为 。那接下来就需要依次求出 时, 的解。
然而正常情况下这样的方程的解应该不止一个,但是在我们要求的问题中,若 ,显然 的不同解都在同一个模 的剩余类上,每个 的解都对应出一个 ,所以在要求 的情况下,显然有 唯一;否则,即 ,此时 ,所以 ,即 ,而 可取任意值。当然,在我们所求的问题中, 取何值都没关系,不妨 。
那么,若已知 ,如何求出 与 呢?显然,。令 ,则 。代入 ,得:。即:。所以:。
这样,只要利用类似于求最小公约数的方法求出上面方程中令 的一组解中的 并将其转换为与其同余的大于等于 小于模数 的整数即可求出 在模 意义下的逆元 。
时间复杂度:。
但是这里有一个细节,那就是最后取模的时候得出的 可能是负数,所以一定要记得判断正负性,因为在 C++ 中,是不能直接对负数取模的。所以,假设已经算出 并将其存储至变量 中,则如果要让 对 取模,不能写成
有一种方法,根据欧拉定理,因为 ,所以可以求出 (求 函数的方法就用模板即可,求幂方法同【三、求出 】中的方法)。虽然这种方法的时间复杂度是 ,足以通过此题,但是其实有一种更优的方法(拓展欧拉定理),在这里介绍一下。
首先,因为 ,所以有 。
注意到,若有一方程 ,其中 ,,且 。则令 ,有 ,则解出的 就是 在模 意义下的逆元,即 在模 意义下的逆元。
那么怎么解出这个方程呢?其实最开始的方程的右边是 ,只是因为我们代入的值使其为 。我们想到,可否使用类似于求最大公因数的方法来求解这个方程呢?
若有 ,且 ,则令 ,得到一个新的方程 (因为 )。直到 ,令此时的 为 。那接下来就需要依次求出 时, 的解。
然而正常情况下这样的方程的解应该不止一个,但是在我们要求的问题中,若 ,显然 的不同解都在同一个模 的剩余类上,每个 的解都对应出一个 ,所以在要求 的情况下,显然有 唯一;否则,即 ,此时 ,所以 ,即 ,而 可取任意值。当然,在我们所求的问题中, 取何值都没关系,不妨 。
那么,若已知 ,如何求出 与 呢?显然,。令 ,则 。代入 ,得:。即:。所以:。
这样,只要利用类似于求最小公约数的方法求出上面方程中令 的一组解中的 并将其转换为与其同余的大于等于 小于模数 的整数即可求出 在模 意义下的逆元 。
时间复杂度:。
但是这里有一个细节,那就是最后取模的时候得出的 可能是负数,所以一定要记得判断正负性,因为在 C++ 中,是不能直接对负数取模的。所以,假设已经算出 并将其存储至变量 中,则如果要让 对 取模,不能写成
x = x % r;,而要写成 x = (x >= 0) ? (x % r) : ((r - (-x) % r) % r)(码风不好,见谅,懂意思就行)。三、求出
好了,接下来就是求出 并输出了(显然,)。但是注意到 。那么,如果按照下面代码片段的方法求 的话,是肯定会超时的(因为时间复杂度是 ):
CPPlong long s = 1;
for(int i = 1; i <= d; i ++) {
s = s * c % n;
}
return s;
所以,需要进行优化。
这里需要用到分治或倍增的思想(有两种实现方式)。
这里需要用到分治或倍增的思想(有两种实现方式)。
分治法
令 。显然当 时,有:
所以:
所以就可以用上面方法递归求出 的值,即 的值。时间复杂度:。
倍增法
因为 ,所以显然存在正整数 ,以及正整数 ,且对于所有的 并且 都有 ,并满足 。所以:
显然,若 ,则:
所以,用一个变量存储答案(初值为 ),从小()到大()循环枚举整数 :每次按照上面方法求出 ,如果 ,则让答案变量乘上 后对 取模。显然,。所以时间复杂度也是 。
所以,正解思路总的时间复杂度就是 ( 表示询问总数)。
代码
在放代码之前,我想说一些细节:
- 记得开
long long!! - 注意是多组数据,想好读入方式。
好了,接下来放代码。
分治法
CPP#include <bits/stdc++.h>
#define int long long
using namespace std;
int n, e, c, r, x, y;
int Phi(int t) {//这道题情况比较特殊,不用打全求欧拉函数的模板
for(int i = 2; ; i ++) {
if(t % i == 0) {
return (i - 1) * (t / i - 1);
}
}
}
void Exgcd(int a, int b) {//拓展欧拉定理求逆元
if(!b) {
x = 1;
y = 0;
return;
}
Exgcd(b, a % b);
int temp = y;
y = x - (a / b) * y;
x = temp;
}
int Quick_pow(int t) {
if(t == 1) return c;
int tmp = Quick_pow(t >> 1);
(tmp *= tmp) %= n;
if(t & 1) (tmp *= c) %= n;
return tmp;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
//读入小优化,不介意吧
while(cin >> n >> e >> c) {
r = Phi(n);
Exgcd(e, r);
cout << Quick_pow((x >= 0) ? (x % r) : ((r - (-x) % r) % r)) << endl;
}
return 0;
}
倍增法
CPP#include <bits/stdc++.h>
#define int long long
using namespace std;
int n, e, c, r, x, y;
int Phi(int t) {//这道题情况比较特殊,不用打全求欧拉函数的模板
for(int i = 2; ; i ++) {
if(t % i == 0) {
return (i - 1) * (t / i - 1);
}
}
}
void Exgcd(int a, int b) {//拓展欧拉定理求逆元
if(!b) {
x = 1;
y = 0;
return;
}
Exgcd(b, a % b);
int temp = y;
y = x - (a / b) * y;
x = temp;
}
int Quick_pow(int t) {
int tmp = c, s = 1;
while(t) {
if(t & 1) (s *= tmp) %= n;
(tmp *= tmp) %= n;
t >>= 1;
}
return s;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
//读入小优化,不介意吧
while(cin >> n >> e >> c) {
r = Phi(n);
Exgcd(e, r);
cout << Quick_pow((x >= 0) ? (x % r) : ((r - (-x) % r) % r)) << endl;
}
return 0;
}
提交记录
总结
本题思路较为简单,只是需要一些数论的知识,照题意模拟即可。代码比较短,自己手打。
相关推荐
评论
共 1 条评论,欢迎与作者交流。
正在加载评论...