数论练习二之BSGS算法——随机数生成器,Matrix,Lunar New Year and a Recursive Sequence,Fermat‘s Last Theorem
[SDOI2013] 隨機數生成器
description
solution
肯定是非常想找一個通項公式來表示第nnn個數的
依據形式,考慮化成等比數列
xi+1+k=a(xi+k)=a?xi+b+t?k=ba?1x_{i+1}+k=a(x_i+k)=a·x_i+b+t\Rightarrow k=\frac{a-1}xi+1?+k=a(xi?+k)=a?xi?+b+t?k=a?1b?
?xi+ba?1=ai?1(x1+ba?1)(modp)?ai?1≡(xi+b?(a?1)?1)?(x1+b?(a?1)?1)?1(modp)\Rightarrow x_i+\frac{a-1}=a^{i-1}(x_1+\frac{a-1})\pmod p\Rightarrow a^{i-1}\equiv (x_i+b·(a-1)^{-1})·(x_1+b·(a-1)^{-1})^{-1}\pmod p?xi?+a?1b?=ai?1(x1?+a?1b?)(modp)?ai?1≡(xi?+b?(a?1)?1)?(x1?+b?(a?1)?1)?1(modp)
xix_ixi?就是題目中的ttt,所以式子只有ai?1a^{i-1}ai?1這個指數未知,bsgs求解最小指數(ppp是質數)
但要先特判a=0/1a=0/1a=0/1兩種情況,顯然此情況下等比數列式子是不成立的
-
a=0
xi=bx_i=bxi?=b
-
a=1
xi≡x1+b(i?1)(modp)?(t?x1)b?1≡i?1(modp)x_i\equiv x_1+b(i-1)\pmod p\Leftrightarrow (t-x_1)b^{-1}\equiv i-1\pmod pxi?≡x1?+b(i?1)(modp)?(t?x1?)b?1≡i?1(modp)
code
#include <map> #include <cmath> #include <cstdio> using namespace std; #define int long long map < int, int > mp;int bsgs( int a, int r, int p ) {if( a % p == 0 ) return -1;mp.clear();int m = ceil( sqrt( p * 1.0 ) );int IS = 1;for( int i = 1;i <= m;i ++ ) {IS = IS * a % p;mp[IS * r % p] = i;}int SI = 1;for( int i = 1;i <= m;i ++ ) {SI = SI * IS % p;if( mp.count( SI ) ) return i * m - mp[SI] + 1;}return -1; }int T, p, a, b, x1, t;int qkpow( int x, int y ) {int ans = 1;while( y ) {if( y & 1 ) ans = ans * x % p;x = x * x % p;y >>= 1;}return ans; }int inv( int x ) {return qkpow( x, p - 2 ); }signed main() {scanf( "%lld", &T );while( T -- ) {scanf( "%lld %lld %lld %lld %lld", &p, &a, &b, &x1, &t );if( x1 == t ) {printf( "1\n" );continue;}if( a == 0 ) {if( x1 == t ) printf( "1\n" );else if( b == t ) printf( "2\n" );else printf( "-1\n" );continue;}if( a == 1 ) {if( b == 0 ) printf( "-1\n" );else printf( "%lld\n", ( ( t - x1 ) % p + p ) % p * inv( b ) % p + 1 );continue;}printf( "%lld\n", bsgs( a, ( t + b * inv( a - 1 ) % p ) * inv( x1 + b * inv( a - 1 ) % p ) % p, p ) );} return 0; }[BZOJ4128]Matrix
description
solution
因為保證了AAA矩陣有逆,所以可以選擇求矩陣的逆
但其實還是選擇兩邊同乘矩陣要好一些
BSGS模板,有優質代碼是寫了非常迅速的hash
但是偶直接map里面塞矩陣,重載一下map的小于等于比較挺不戳的!
code
#include <map> #include <cmath> #include <cstdio> #include <cstring> using namespace std; int n, mod; struct matrix {int c[71][71];void clear() {memset( c, 0, sizeof( c ) );}matrix() {clear();}void init() {for( int i = 1;i <= n;i ++ ) {for( int j = 1;j <= n;j ++ )c[i][j] = 0;c[i][i] = 1;}}matrix operator * ( const matrix &t ) const {matrix ans;for( int i = 1;i <= n;i ++ )for( int j = 1;j <= n;j ++ )for( int k = 1;k <= n;k ++ )ans.c[i][j] = ( ans.c[i][j] + c[i][k] * t.c[k][j] ) % mod;return ans;}friend bool operator == ( const matrix &s, const matrix &t ) {for( int i = 1;i <= n;i ++ )for( int j = 1;j <= n;j ++ )if( s.c[i][j] ^ t.c[i][j] ) return 0;return 1;}friend bool operator < ( const matrix &s, const matrix &t ) {for( int i = 1;i <= n;i ++ )for( int j = 1;j <= n;j ++ )if( s.c[i][j] ^ t.c[i][j] ) return s.c[i][j] < t.c[i][j];return 0;} }A, B, g, h; map < matrix, int > mp;int main() {scanf( "%d %d", &n, &mod );for( int i = 1;i <= n;i ++ )for( int j = 1;j <= n;j ++ )scanf( "%d", &A.c[i][j] );for( int i = 1;i <= n;i ++ )for( int j = 1;j <= n;j ++ )scanf( "%d", &B.c[i][j] );int m = ceil( sqrt( mod * 1.0 ) );g.init();for( int i = 1;i <= m;i ++ ) {g = g * A;h = g * B;mp[h] = i;}h.init();for( int i = 1;i <= m;i ++ ) {h = h * g;if( mp.count( h ) ) return ! printf( "%d\n", i * m - mp[h] );}return 0; }CF1106F Lunar New Year and a Recursive Sequence
problem
solution
fi=∏j=1kfi?jbj(modp)f_{i}=\prod_{j=1}^kf_{i-j}^{b_j}\pmod pfi?=∏j=1k?fi?jbj??(modp)
ppp是非常特殊的質數,其原根為333,可以用原根改寫上式,令3gi=fi3^{g_i}=f_i3gi?=fi?
gi=∑j=1kgi?j?bj(modφ(p))g_i=\sum_{j=1}^kg_{i-j}·b_j\pmod {\varphi(p)}gi?=∑j=1k?gi?j??bj?(modφ(p))
此時的ggg可以用矩陣加速,初始化fi=0(i<k),fk=1f_i=0(i<k),f_k=1fi?=0(i<k),fk?=1,轉移n?kn-kn?k次變成[fn?k+1,...,fn][f_{n-k+1},...,f_n][fn?k+1?,...,fn?]
[hi?k+1,...,hi?k]×[00...0bk10...0bk?101...0bk?2...............00...0b200...1b1]=[hi+k+2,...,hi+1?k]\begin{bmatrix} \underbrace{h_{i-k+1},...,h_i}_{k} \end{bmatrix}\times \begin{bmatrix} 0&0&...&0&b_k\\ 1&0&...&0&b_{k-1}\\ 0&1&...&0&b_{k-2}\\ ...&...&...&...&...\\ 0&0&...&0&b_2\\ 0&0&...&1&b_1 \end{bmatrix}= \begin{bmatrix} \underbrace{h_{i+k+2},...,h_{i+1}}_{k} \end{bmatrix} [khi?k+1?,...,hi????]×?????????010...00?001...00?..................?000...01?bk?bk?1?bk?2?...b2?b1???????????=[khi+k+2?,...,hi+1????]
同時,有fkt≡fn(modp),3gk=fkf_k^t\equiv f_n\pmod p,3^{g_k}=f_kfkt?≡fn?(modp),3gk?=fk?,這里的ttt矩等于加速矩陣自乘后的hkh_khk?
fkT=3gk?T≡fn=3gn(modp)f_k^T=3^{g_k·T}\equiv f_n=3^{g_n}\pmod pfkT?=3gk??T≡fn?=3gn?(modp)
對于3gn≡fn(modp)3^{g_n}\equiv f_n\pmod p3gn?≡fn?(modp)的gng_ngn?未知,用BSGS求得
對于指數用exgcd計算,gk×t≡gn(modp?1)?t×gk?(p?1)×y=gng_k\times t\equiv g_n\pmod {p-1}\Leftrightarrow t\times g_k-(p-1)\times y=g_ngk?×t≡gn?(modp?1)?t×gk??(p?1)×y=gn?
未知數為gk,yg_k,ygk?,y,求出后fk=3gkf_k=3^{g_k}fk?=3gk?
code
#include <map> #include <cmath> #include <cstdio> #include <cstring> using namespace std; #define int long long #define mod 998244353 struct matrix {int n, m;int c[101][101];matrix() {memset( c, 0, sizeof( c ) );}matrix operator * ( matrix &t ) const {matrix ans;ans.n = n, ans.m = t.m;for( int i = 1;i <= n;i ++ )for( int j = 1;j <= t.m;j ++ )for( int k = 1;k <= m;k ++ )ans.c[i][j] = ( ans.c[i][j] + c[i][k] * t.c[k][j] ) % ( mod - 1 );return ans;} }h, t;matrix qkpow( matrix x, int y ) {matrix ans;ans.n = ans.m = x.n;for( int i = 1;i <= ans.n;i ++ )ans.c[i][i] = 1;while( y ) {if( y & 1 ) ans = ans * x;x = x * x;y >>= 1;}return ans; }map < int, int > mp;int bsgs( int a, int r ) {int m = ceil( sqrt( mod * 1.0 ) );int IS = 1;for( int i = 1;i <= m;i ++ ) {IS = IS * a % mod;mp[IS * r % mod] = i;}int MS = 1;for( int i = 1;i <= m;i ++ ) {MS = MS * IS % mod;if( mp.count( MS ) )return i * m - mp[MS];}return -1; }int exgcd( int a, int b, int &x, int &y ) {if( ! b ) {x = 1, y = 0;return a;}else {int d = exgcd( b, a % b, y, x );y -= a / b * x;return d;} }int qkpow( int x, int y ) {int ans = 1;while( y ) {if( y & 1 ) ans = ans * x % mod;x = x * x % mod;y >>= 1;}return ans; }signed main() {int k, n, fn;scanf( "%lld", &k );h.n = h.m = t.m = k, t.n = t.c[1][k] = 1;for( int i = 2;i <= k;i ++ )h.c[i][i - 1] = 1;for( int i = k;i;i -- )scanf( "%lld", &h.c[i][k] );scanf( "%lld %lld", &n, &fn );h = qkpow( h, n - k );t = t * h;int g = bsgs( 3, fn );int x, y, d = exgcd( t.c[1][k], mod - 1, x, y );if( g % d ) return ! printf( "-1\n" );else {x = ( x * ( g / d ) % ( mod - 1 ) + ( mod - 1 ) ) % ( mod - 1 );printf( "%lld\n", qkpow( 3, x ) );}return 0; }Fermat’s Last Theorem
description
solution
找出ppp的原根ggg,令x=ga,y=gb,z=gcx=g^a,y=g^b,z=g^cx=ga,y=gb,z=gc
則gan+gbn≡gcn(modp)g^{an}+g^{bn}\equiv g^{cn}\pmod pgan+gbn≡gcn(modp)
令r=gn?ra+rb≡rc(modp)r=g^n\Rightarrow r^a+r^b\equiv r^c\pmod pr=gn?ra+rb≡rc(modp)
假設a≤ba\le ba≤b
- a≤c?1+rb?a≡rc?a(modp)a\le c\Rightarrow 1+r^{b-a}\equiv r^{c-a}\pmod pa≤c?1+rb?a≡rc?a(modp)
- a>c?ra?c+rb?a≡1(modp)a>c\Rightarrow r^{a-c}+r^{b-a}\equiv 1\pmod pa>c?ra?c+rb?a≡1(modp)
枚舉rkr^krk,出現循環的時候就break
出現1+rk1≡rk2/rk1+rk2≡1(modp)1+r^{k_1}\equiv r^{k_2}/r^{k_1}+r^{k_2}\equiv 1\pmod p1+rk1?≡rk2?/rk1?+rk2?≡1(modp)就是有解了
code
#include <cstdio> #define int long long #define maxn 1000005 int d[maxn], vis[maxn], mi[maxn];int qkpow( int x, int y, int mod ) {int ans = 1;while( y ) {if( y & 1 ) ans = ans * x % mod;x = x * x % mod;y >>= 1;}return ans; }int root( int p ) {int cnt = 0, phi = p - 1;for( int i = 2;i * i <= phi;i ++ )if( phi % i == 0 ) {d[++ cnt] = i;if( i * i != phi ) d[++ cnt] = phi / i;}for( int g = 1, i;;g ++ ) {for( i = 1;i <= cnt;i ++ )if( qkpow( g, d[i], p ) == 1 )break;if( i > cnt ) return g;} }signed main() {int T, n, p;scanf( "%lld", &T );for( int Case = 1;Case <= T;Case ++ ) {scanf( "%lld %lld", &n, &p );int g = root( p ), r = qkpow( g, n, p );int t = 1, x = -1, y, z;for( int i = 0;i < p;i ++ ) {if( i ) t = t * r % p;if( vis[t] == Case ) break;vis[t] = Case, mi[t] = i;int MS = 1 + t;if( MS >= p ) MS = 0;int IS = 1 - t + p;if( IS >= p ) IS = 0;if( vis[MS] == Case ) {x = 1, y = qkpow( g, i, p ), z = qkpow( g, mi[MS], p );break;}if( vis[IS] == Case ) {x = qkpow( g, i, p ), y = qkpow( g, mi[IS], p ), z = 1;break;}}if( ~ x ) printf( "%lld %lld %lld\n", x, y, z );else printf( "-1\n" );}return 0; }總結
以上是生活随笔為你收集整理的数论练习二之BSGS算法——随机数生成器,Matrix,Lunar New Year and a Recursive Sequence,Fermat‘s Last Theorem的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux总线错误(linux总线)
- 下一篇: 线性代数五之高斯消元——[SDOI201