bzoj 3122 随机数生成器 - BSGS
Description
Input
输入含有多组数据,第一行一个正整数T,表示这个测试点内的数据组数。
接下来T行,每行有五个整数p,a,b,X1,t,表示一组数据。保证X1和t都是合法的页码。
注意:P一定为质数
Output
共T行,每行一个整数表示他最早读到第t页是哪一天。如果他永远不会读到第t页,输出-1。
Sample Input
7 1 1 3 3
7 2 2 2 0
7 2 2 2 1
Sample Output
1
3
-1
HINT
0<=a<=P-1,0<=b<=P-1,2<=P<=10^9
题目大意
给定一个序列$X$的首项$x_{1}$,序列$X$满足递推关系$x_{n + 1} = (a\cdot x_{n} + b)\mod p$,其中这里的$mod$表示求余数。问序列中哪个数第一次为$t$,或者输出无解。
显然递推关系对方程不是特别友好,所以考虑求序列的通项。
由于这里用特征根的方法来求会很繁琐,所以直接展开:
$x_{n} = \left ( a\cdot x_{n - 1} + b \right ) \mod p$
$x_{n} = \left ( a^{2}\cdot x_{n - 2} + ab + b \right ) \mod p$
$x_{n} = \left ( a^{n - 1}\cdot x_{1} + a^{n-2}b + \cdots + ab + b \right ) \mod p$
用等比数列求和公式得到:
$x_{n} = \left [ a^{n - 1}\cdot x_{1} + \frac{\left (a^{n - 1} - 1 \right )b}{a - 1}\right ] \mod p\ \ \ (a \neq 1)$
$x_{n} = \frac{a^{n - 1}\cdot x_{1}\left ( a - 1 \right ) + \left (a^{n - 1} - 1 \right )b}{a - 1} \mod p\ \ \ (a \neq 1)$
$x_{n} = \frac{a^{n - 1}\cdot\left [ x_{1}\left ( a - 1 \right ) + b \right ] - b}{a - 1} \mod p\ \ \ (a \neq 1)$
然后再移项:
$a^{n - 1}\cdot\left [ x_{1}\left ( a - 1 \right ) + b \right ] \equiv \left ( a - 1 \right )x_{n} + b \pmod{p}\ \ \ (a \neq 1)$
BSGS解方程即可。
继续考虑$a = 1$的情况,此时有:
$x_{n} \equiv x_{1} + \left ( n - 1 \right )b \pmod{p} \ \ \left ( a = 1 \right )$
写成不定方程的形式:
$n\cdot b + k\cdot p = x_{n}+ b - x_{1}\ \ \left ( a = 1 \right )$
直接扩欧算答案。
注意一个细节,扩欧如果算出答案模$p$为0,那么应该输出$p$,因为这里求的是最小正余数。
似乎$a = 0$的时候,要加点处理,不然扔进去的东西变成负数,BSGS也会出事情。干脆直接特判。
Code
1 /** 2 * bzoj 3 * Problem#3122 4 * Accepted 5 * Time: 268ms 6 * Memory: 2028k 7 */ 8 #include <iostream> 9 #include <cstring> 10 #include <cstdio> 11 #include <cmath> 12 using namespace std; 13 typedef bool boolean; 14 15 typedef class HashMap { 16 private: 17 static const int M = 46666; 18 public: 19 int ce; 20 int h[M], key[M], val[M], next[M]; 21 22 HashMap() { } 23 24 void insert(int k, int v) { 25 int ha = k % M; 26 for (int i = h[ha]; ~i; i = next[i]) 27 if (key[i] == k) { 28 val[i] = v; 29 return; 30 } 31 ++ce, key[ce] = k, val[ce] = v, next[ce] = h[ha], h[ha] = ce; 32 } 33 34 int operator [] (int k) { 35 int ha = k % M; 36 for (int i = h[ha]; ~i; i = next[i]) 37 if (key[i] == k) 38 return val[i]; 39 return -1; 40 } 41 42 void clear() { 43 ce = -1; 44 memset(h, -1, sizeof(h)); 45 } 46 }HashMap; 47 48 49 int qpow(int a, int pos, int m) { 50 int pa = a, rt = 1; 51 for ( ; pos; pos >>= 1, pa = pa * 1ll * pa % m) 52 if (pos & 1) 53 rt = rt * 1ll * pa % m; 54 return rt; 55 } 56 57 int gcd (int a, int b) { 58 return (b) ? (gcd(b, a % b)) : (a); 59 } 60 61 void exgcd(int a, int b, int& d, int &x, int &y) { 62 if (!b) 63 d = a, x = 1, y = 0; 64 else { 65 exgcd(b, a % b, d, y, x); 66 y -= (a / b) * x; 67 } 68 } 69 70 int inv(int a, int n) { 71 int d, x, y; 72 exgcd(a, n, d, x, y); 73 return (x < 0) ? (x + n) : (x); 74 } 75 76 int T; 77 int p, a, b, x1, t; 78 79 inline void init() { 80 scanf("%d%d%d%d%d", &p, &a, &b, &x1, &t); 81 } 82 83 HashMap mp; 84 inline int ind(int x, int a, int p, int pro) { 85 mp.clear(); 86 int cs = sqrt(p - 1 + 0.5); 87 int ainv = inv(x, p), iap = a * 1ll * qpow(ainv, cs - 1, p) % p; 88 for (int i = cs - 1; ~i; i--, iap = iap * 1ll * x % p) 89 mp.insert(iap, i); 90 int cp = qpow(x, cs, p), pw = pro; 91 for (int i = 0; i < p; i += cs, pw = pw * 1ll * cp % p) 92 if (~mp[pw]) 93 return mp[pw] + i; 94 return -2; 95 } 96 97 inline int solve1() { 98 if (!b) return (t == x1) ? (1) : (-1); 99 int d, x, y, go = t + b - x1; 100 exgcd(b, p, d, x, y); 101 x = x * 1ll * go % p; 102 return (x <= 0) ? (x + p) : (x); 103 } 104 105 inline void solve() { 106 if (a == 0) 107 printf("%d\n", (t == x1) ? (1) : ((b == t) ? (2) : (-1))); 108 else if (a == 1) 109 printf("%d\n", solve1()); 110 else 111 printf("%d\n", ind(a, ((a - 1) * 1ll * t + b) % p, p, (x1 * 1ll * (a - 1) + b) % p) + 1); 112 } 113 114 int main() { 115 scanf("%d", &T); 116 while (T--) { 117 init(); 118 solve(); 119 } 120 return 0; 121 }