BSGS及其拓展
BSGS
介紹
這是一個(gè)求解ax≡b(modp)a ^ {x} \equiv b \pmod pax≡b(modp),的方法。并且ppp是質(zhì)數(shù),a,pa, pa,p互質(zhì),費(fèi)馬小定理可知,這個(gè)式子有周期性,
我們一般取m=sqrt(p)m = sqrt(p)m=sqrt(p),假設(shè)x=i?m+j,0<=i,j<=mx = i * m + j, 0 <= i, j <= mx=i?m+j,0<=i,j<=m,則有
ai?m+j≡b(modp)a ^ {i * m + j} \equiv b \pmod pai?m+j≡b(modp)
ai+m≡baj(modp)a ^ {i + m} \equiv \frac {a ^ j} \pmod pai+m≡ajb?(modp)
為了方便我們?cè)O(shè)x=i?m?jx = i * m - jx=i?m?j,則有
ai?m≡b?aj(modp)a ^ {i * m} \equiv b * a ^ {j} \pmod pai?m≡b?aj(modp)
所以我們只要通過一次枚舉jjj,記錄下b?ajb * a ^ jb?aj,再一次枚舉iii去剛才枚舉過的jjj中尋找有沒有符合要求的答案即可,整體復(fù)雜度是p\sqrt {p}p?的
P2485 [SDOI2011]計(jì)算器
模板題
/*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??褪钇诙嘈S?xùn)練營(yíng)(第五場(chǎng))C generator 2
思路
x0=x0x_0 = x_0x0?=x0?
x1=a?x0?bx_1 = a * x_0 * bx1?=a?x0??b
x2=a?x1+b=a2?x0+a?b+bx_2 = a * x_1 + b = a ^{2} * x_0 + a * b + bx2?=a?x1?+b=a2?x0?+a?b+b
容易發(fā)現(xiàn)后項(xiàng)是一個(gè)等比數(shù)列求和
xn=anx0+b(1?an)1?ax_n = a ^ {n} x_0 + \frac {b (1 - a ^ n)} {1 - a}xn?=anx0?+1?ab(1?an)?
我們要求xn=vx_n = vxn?=v,化簡(jiǎn)
anx0+b(1?an)1?a=va ^ {n} x_0 + \frac {b (1 - a ^ n)} {1 - a} = vanx0?+1?ab(1?an)?=v
anx0(1?a)+b(1?an)=v(1?a)a ^{n} x_0(1 - a) + b (1 - a ^ n) = v(1 - a)anx0?(1?a)+b(1?an)=v(1?a)
an(x0?ax0?b)=v(1?a)?ba ^ n (x_0 - ax_0 - b) = v(1 - a) - ban(x0??ax0??b)=v(1?a)?b
an=v(1?a)?bx0?ax0?ba^n =\frac {v (1 - a) - b} {x_0 - a x_0 - b}an=x0??ax0??bv(1?a)?b?
上面式子都是modp\mod pmodp下的同余等式,為了方便寫了===
看到這里就簡(jiǎn)單了,我們要求解的是nnn,顯然右邊這一坨都是已知的,我們假定為BBB,求解an=Ba ^ n = Ban=B,這不就是個(gè)裸題了嗎。
這道題目還要稍加分類討論一下:
- a == 0
x0=x0,xi=b(i>=1)x_0 = x_0, x_i = b(i >= 1)x0?=x0?,xi?=b(i>=1)
- a==1a == 1a==1
因?yàn)檫@種情況上面不能直接相除,所以我們需要特殊考慮ai=b+i?aa_i = b + i * aai?=b+i?a
也就是求解i?a=v?bi * a = v - bi?a=v?b,這個(gè)時(shí)候只要左右兩邊同時(shí)乘以aaa的逆元即可得到我們要的iii
代碼
/*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拓展
介紹
求解ax≡b(modp)a ^ x \equiv b \pmod pax≡b(modp),但是a,ba, ba,b不互質(zhì),這里就要用到我們的EXBSGSEXBSGSEXBSGS了。
假定ax+py=ba ^ x + py = bax+py=b
d1=gcd(a,p),如果有解則一定:gcd(a,p)∣bd_1 = gcd(a, p),如果有解則一定:gcd(a, p) \mid bd1?=gcd(a,p),如果有解則一定:gcd(a,p)∣b
得到ax?1ad1+pd1y=bd1a ^{x - 1}\frac{a} {d_1} + \frac{p}{d_1}y = \frac {d_1}ax?1d1?a?+d1?p?y=d1?b?
如果gcd(a,pd1)!=1gcd(a, \frac{p} {d_1}) != 1gcd(a,d1?p?)!=1,繼續(xù)化簡(jiǎn)
d2=gcd(a,pd1)?>ax?2a2d1d2+pd1d2y=bd1d2d_2 = gcd(a, \frac{p} {d_1})->a ^{x - 2} \frac{a ^ 2} {d_1d_2} + \frac{p} {d_1d_2}y = \frac{d_1d_2}d2?=gcd(a,d1?p?)?>ax?2d1?d2?a2?+d1?d2?p?y=d1?d2?b?
如果gcd(a,p2d1d2)!=1gcd(a, \frac{p ^ 2} {d_1d_2}) != 1gcd(a,d1?d2?p2?)!=1
重復(fù)上面操作,最后得到式子
ax?nand1d2……dn+pd1d2……dny=bd1d2……dna ^{x - n} \frac {a ^ n} {d_1d_2……d_n} + \frac{p}{d_1d_2……d_n}y = \frac{d_1d_2……d_n}ax?nd1?d2?……dn?an?+d1?d2?……dn?p?y=d1?d2?……dn?b?
記A=ax?n,A′=and1d2……dn,P=pd1d2……dn,B=bd1d2……dnA = a {x - n}, A' = \frac{a^n}{d_1d_2……d_n}, P = \frac{p}{d_1d_2……d_n}, B = \frac {d_1d_2……d_n}A=ax?n,A′=d1?d2?……dn?an?,P=d1?d2?……dn?p?,B=d1?d2?……dn?b?
則變成里求Ax?n≡BA′?1(modP)A^{x - n} \equiv B A'^{-1} \pmod{P}Ax?n≡BA′?1(modP)
記錄進(jìn)行了多少次gcdgcdgcd求解,通過求A′A'A′的逆元化簡(jiǎn)式子,再通過一次BSGSBSGSBSGS求得x?nx - nx?n,即可得到我們得答案xxx
P4195 【模板】擴(kuò)展BSGS
一定注意求逆元不能用費(fèi)馬小定理,我就入了這個(gè)坑。
/*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; }總結(jié)
- 上一篇: QT QDir 基本函数使用
- 下一篇: 现代密码学实验五:签名算法