BSGS
介绍
这是一个求解 ,的方法。并且 是质数, 互质,费马小定理可知,这个式子有周期性,
我们一般取 ,假设 ,则有
为了方便我们设 ,则有
所以我们只要通过一次枚举 ,记录下 ,再一次枚举 去刚才枚举过的 中寻找有没有符合要求的答案即可,整体复杂度是 的
P2485 [SDOI2011]计算器
模板题
/*
Author : lifehappy
*/
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
// #include <cstdio>
// #include <iostream>
// #include <stdlib.h>
// #include <algorithm>
// #include <cmath>
#define mp make_pair
#define pb push_back
#define endl '\n'
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const double pi = acos(-1.0);
const double eps = 1e-7;
const int inf = 0x3f3f3f3f;
inline ll read() {
ll f = 1, x = 0;
char c = getchar();
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return f * x;
}
void print(ll x) {
if(x < 10) {
putchar(x + 48);
return ;
}
print(x / 10);
putchar(x % 10 + 48);
}
ll quick_pow(ll a, ll n, ll mod) {
ll ans = 1;
while(n) {
if(n & 1) ans = (ans * a) % mod;
n >>= 1;
a = (a * a) % mod;
}
return ans;
}
ll quick_mult(ll a, ll b, ll mod) {
ll ans = 0;
while(b) {
if(b & 1) ans = (ans + a) % mod;
b >>= 1;
a = (a + a) % mod;
}
return ans;
}
ll ex_gcd(ll a, ll b, ll & x, ll & y) {
if(!b) {
x = 1, y = 0;
return a;
}
ll gcd = ex_gcd(b, a % b, x, y);
ll temp = x;
x = y;
y = temp - a / b * y;
return gcd;
}
void BSGC(ll a, ll b, ll p) {
map<ll, ll> MP;
int m = sqrt(p) + 1;
ll x = b, nex = quick_pow(a, m, p);
for(int i = 0; i <= m; i++) {
MP[x] = i;
x = (x * a) % p;
}
x = 1;
if(nex == 0) {
if(b == 0) {
puts("0");
}
else {
puts("Orz, I cannot find x!");
}
return ;
}
for(int i = 0; i <= m; i++) {
if(MP.count(x)) {
printf("%d\n", i * m - MP[x]);
return ;
}
x = (x * nex) % p;
}
puts("Orz, I cannot find x!");
}
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
// ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int n = read(), k = read();
for(int i = 1; i <= n; i++) {
ll a = read(), b = read(), p = read();
if(k == 1) {
printf("%lld\n", quick_pow(a, b, p));
}
else if(k == 2) {
ll x, y;
ll gcd = ex_gcd(a, p, x, y);
if(b % gcd) {
puts("Orz, I cannot find x!");
}
else {
b /= gcd;
x = (((x % p + p) % p) * b) % p;
printf("%lld\n", x);
}
}
else {
BSGC(a, b % p, p);
}
}
return 0;
}
2019牛客暑期多校训练营(第五场)C generator 2
思路
容易发现后项是一个等比数列求和
我们要求 ,化简
上面式子都是 下的同余等式,为了方便写了
看到这里就简单了,我们要求解的是 ,显然右边这一坨都是已知的,我们假定为 ,求解 ,这不就是个裸题了吗。
这道题目还要稍加分类讨论一下:
- a == 0
因为这种情况上面不能直接相除,所以我们需要特殊考虑
也就是求解 ,这个时候只要左右两边同时乘以 的逆元即可得到我们要的
代码
/*
Author : lifehappy
*/
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define endl '\n'
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const double pi = acos(-1.0);
const double eps = 1e-7;
const int inf = 0x3f3f3f3f;
inline ll read() {
ll f = 1, x = 0;
char c = getchar();
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return f * x;
}
void print(ll x) {
if(x < 10) {
putchar(x + 48);
return ;
}
print(x / 10);
putchar(x % 10 + 48);
}
ll quick_pow(ll a, ll n, ll mod) {
ll ans = 1;
while(n) {
if(n & 1) ans = (ans * a) % mod;
a = (a * a) % mod;
n >>= 1;
}
return ans;
}
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
// ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int T = read();
while(T--) {
ll n = read(), x0 = read(), a = read(), b = read(), p = read();
ll x = 1, Unit = quick_pow(a, 1000, p);
unordered_map<ll, int> MP;
for(int i = 1; i <= 1000000; i++) {
x = (x * Unit) % p;
if(!MP.count(x)) {
MP[x] = i * 1000;
}
}
ll inv = quick_pow(((x0 - a * x0 - b) % p + p) % p, p - 2, p);
int t = read();
while(t--) {
ll v = read();
if(a == 0) {
if(v % p == x0 % p) {
puts("0");
}
else if(v % p == b % p) {
puts("1");
}
else {
puts("-1");
}
continue;
}
if(a == 1) {
ll ans = (((((v - x0) % p + p) % p) * quick_pow(b, p - 2, p))) % p;
if(ans < n) {
printf("%lld\n", ans);
}
else {
puts("-1");
}
continue;
}
v = (((v * (1 - a) - b) % p + p) % p * inv) % p;
if(Unit == 0) {
if(v == 0) {
puts("0");
}
else {
puts("-1");
}
continue;
}
x = v;
int ans = p + 1;
for(int i = 0; i <= 1000; i++) {
if(MP.count(x)) {
ans = min(ans, MP[x] - i);
}
x = (x * a) % p;
}
if(ans != p + 1 && ans < n) {
printf("%d\n", ans);
}
else {
puts("-1");
}
}
}
return 0;
}
BSGS拓展
介绍
求解 ,但是 不互质,这里就要用到我们的 了。
假定
得到
如果 ,继续化简
如果
重复上面操作,最后得到式子
记
则变成里求
记录进行了多少次 求解,通过求 的逆元化简式子,再通过一次 求得 ,即可得到我们得答案
P4195 【模板】扩展BSGS
一定注意求逆元不能用费马小定理,我就入了这个坑。
/*
Author : lifehappy
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll gcd(ll a, ll b) {
return b ? gcd(b, a % b) : a;
}
ll quick_pow(ll a, ll n, ll mod) {
ll ans = 1;
while(n) {
if(n & 1) ans = (ans * a) % mod;
a = (a * a) % mod;
n >>= 1;
}
return ans;
}
int exgcd(int a, int b, int & x, int & y) {
if(!b) {
x = 1, y = 0;
return a;
}
int gcd = exgcd(b, a % b, x, y);
int temp = x;
x = y;
y = temp - a / b * y;
return gcd;
}
int inv(int a, int b) {
int x, y;
exgcd(a, b, x, y);
return (x % b + b) % b;
}
int BSGS(ll a, ll b, ll p) {
int m = sqrt(p) + 1;
unordered_map<int, int> mp;
int x = b;
for(int i = 0; i <= m; i++) {
mp[x] = i;
x = (1ll * x * a) % p;
}
x = 1;
int Unit = quick_pow(a, m, p);
if(Unit == 0) {
if(b == 0) {
return 0;
}
else {
return -1;
}
}
for(int i = 1; i <= m; i++) {
x = (1ll * x * Unit) % p;
if(mp.count(x)) {
return i * m - mp[x];
}
}
return -1;
}
void EXBSGS(ll a, ll b, ll p) {
a %= p, b %= p;
if(b == 1 || p == 1) {
puts("0");
return ;
}
int cnt = 0, d, ad = 1;
while((d = gcd(a, p)) != 1) {
if(b % d) {
puts("No Solution");
return ;
}
cnt++;
b /= d, p /= d;
ad = (1ll * ad * a / d) % p;
if(ad == b) {
printf("%d\n", cnt);
return ;
}
}
int ans = BSGS(a % p, (1ll * b * inv(ad, p)) % p, p);
if(ans == -1) {
puts("No Solution");
}
else {
printf("%d\n", ans + cnt);
}
}
int main() {
// freopen("in.txt", "r", stdin);
ll a, b, p;
while(scanf("%lld %lld %lld", &a, &p, &b) && (a || b || p)) {
EXBSGS(a, b, p);
}
return 0;
}