P4777 【模板】扩展中国剩余定理(EXCRT)
题目描述
P4777 【模板】扩展中国剩余定理(EXCRT) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
运行代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef __int128 LL;
const int N = 100005;
LL n, m[N], r[N];
LL exgcd(LL a,LL b,LL &x,LL &y){
if(b==0){x=1, y=0; return a;}
LL d, x1, y1;
d = exgcd(b, a%b, x1, y1);
x = y1, y = x1-a/b*y1;
return d;
}
LL EXCRT(LL m[], LL r[]){
LL m1, m2, r1, r2, p, q;
m1 = m[1], r1 = r[1];
for(int i=2; i<=n; i++){
m2 = m[i], r2 = r[i];
LL d = exgcd(m1,m2,p,q);
if((r2-r1)%d){return -1;}
p=p*(r2-r1)/d; //特解
p=(p%(m2/d)+m2/d)%(m2/d);
r1 = m1*p+r1;
m1 = m1*m2/d;
}
return (r1%m1+m1)%m1;
}
int main(){
scanf("%lld", &n);
for(int i = 1; i <= n; ++i)
scanf("%lld%lld", m+i, r+i);
printf("%lld\n",EXCRT(m,r));
return 0;
}
代码思路
-
定义了一些必要的变量和常量,包括
__int128
类型的LL
用于处理大整数,以及数组m
和r
来存储问题中的模数和余数。 -
exgcd
函数是扩展欧几里得算法,用于计算两个数的最大公约数d
以及满足贝祖等式的系数x
和y
。 -
EXCRT
函数是求解中国剩余定理(Chinese Remainder Theorem,CRT)的核心函数:- 首先初始化
m1
和r1
为第一组模数和余数。 - 然后通过循环处理后续的每组模数和余数:计算当前处理的两组模数
m1
和m2
的最大公约数d
。检查当前两组余数的差(r2 - r1)
是否能被d
整除,如果不能则表示无解,返回-1
。计算满足方程的特解p
。更新新的余数r1
和模数m1
。
- 首先初始化
-
在
main
函数中:读取输入的组数n
。取每组的模数和余数。调用EXCRT
函数求解并输出结果。
P3846 [TJOI2007] 可爱的质数/【模板】BSGS
题目描述
P3846 [TJOI2007] 可爱的质数/【模板】BSGS - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
运行代码
#include<iostream>
#include<algorithm>
using namespace std;
#include<unordered_map>
typedef long long ll;
ll bsgs(ll a, ll b, ll p) {
a %= p; b %= p;
if (b == 1)return 0;
ll m = ceil(sqrt(p));
ll t = b;
unordered_map<int, int>hash;
hash[b] = 0;
for (int j = 1; j < m; j++) {
t = t * a % p;
hash[t] = j;
}
ll mi = 1;
for (int i = 1; i <= m; i++)
mi = mi * a % p; //求a^m
t = 1;
for (int i = 1; i <= m; i++) {
t = t * mi % p; //求(a^m)^i
if (hash.count(t))
return i * m - hash[t];
}
return -1; //无解
}
int main() {
int a, p, b;
cin >> p >> a >> b;
int res = bsgs(a, b, p);
if (res == -1) puts("no solution");
else cout << res << endl;
return 0;
}
代码思路
-
定义了
bsgs
函数用于求解形如a^x ≡ b (mod p)
的离散对数问题。 -
在函数内部:
- 首先对
a
和b
取模p
,进行预处理。 - 如果
b
模p
后等于1
,直接返回0
。 - 计算一个步长
m
,约为p
的平方根向上取整。 - 然后通过一个循环计算
b * a^j (j = 0 到 m - 1)
的值,并将其和对应的索引j
存入哈希表hash
中。 - 计算
a^m
并赋值给mi
。 - 再通过一个循环计算
(a^m)^i
,并检查其是否在之前的哈希表中。如果在,就返回计算得到的结果。 - 如果整个过程都没有找到解,返回
-1
。
- 首先对
-
在
main
函数中:读取输入的p
、a
和b
。调用bsgs
函数求解,并根据结果输出相应的信息。如果结果为-1
,输出 "no solution" ,否则输出计算得到的解。
总的来说,这段代码的思路是通过分块和哈希表来加速对离散对数问题的求解。
P4195 【模板】扩展 BSGS/exBSGS
题目描述
P4195 【模板】扩展 BSGS/exBSGS - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
运行代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <unordered_map>
using namespace std;
typedef long long LL;
LL gcd(LL a, LL b){
return b==0?a:gcd(b,a%b);
}
LL exbsgs(LL a, LL b, LL p){
a %= p; b %= p;
if(b==1||p==1)return 0;//x=0
LL d, k=0, A=1;
while(true){
d = gcd(a,p);
if(d==1) break;
if(b%d) return -1; //无解
k++; b/=d; p/=d;
A = A*(a/d)%p; //求a^k/D
if(A==b) return k;
}
LL m=ceil(sqrt(p));
LL t = b;
unordered_map<int,int> hash;
hash[b] = 0;
for(int j = 1; j < m; j++){
t = t*a%p; //求b*a^j
hash[t] = j;
}
LL mi = 1;
for(int i = 1; i <= m; i++)
mi = mi*a%p; //求a^m
t = A;
for(int i=1; i <= m; i++){
t = t*mi%p; //求(a^m)^i
if(hash.count(t))
return i*m-hash[t]+k;
}
return -1; //无解
}
int main(){
LL a, p, b;
while((scanf("%lld%lld%lld",&a,&p,&b)!=EOF)&&a){
LL res = exbsgs(a, b, p);
if(res == -1) puts("No Solution");
else printf("%lld\n",res);
}
return 0;
}
代码思路
-
gcd
函数:这是一个递归实现的求最大公约数的函数,使用欧几里得算法。 -
exbsgs
函数:- 首先对输入的
a
、b
进行模p
的处理。 - 如果
b
为1
或者p
为1
,直接返回0
。 - 然后进入一个循环,不断计算
a
和p
的最大公约数d
。 - 如果
b
不能被d
整除,说明无解,返回-1
。 - 否则更新
k
、b
、p
和A
的值。如果A
等于b
,返回k
。 - 计算一个步长
m
,约为p
的平方根向上取整。 - 接着创建一个哈希表,通过循环计算并存储
b * a^j (j = 0 到 m - 1)
的值和对应的索引。 - 计算
a^m
并赋值给mi
。 - 再通过一个循环计算
A * (a^m)^i
,并检查其是否在之前的哈希表中。如果在,返回计算得到的结果,即i*m - hash[t] + k
。 - 如果整个过程都没有找到解,返回
-1
。
- 首先对输入的
-
main
函数:不断读取输入的a
、p
和b
,只要a
不为0
。调用exbsgs
函数求解,并根据结果输出相应的信息。如果结果为-1
,输出 "No Solution" ,否则输出计算得到的解。
这段代码的主要目的是通过扩展的 Baby Step Giant Step 算法来求解形如 a^x ≡ b (mod p)
的离散对数问题。