洛谷 P3306 【随机数生成器】


观察题目,易得出暴力代码,但是会TLE,只能得20pts

 1 #include<bits/stdc++.h>
 2 #define ll long long
 3 using namespace std;
 4 int T;
 5 ll a, b, x1, t, p, ans;
 6 int main()
 7 {
 8     scanf("%d", &T);
 9     while(T--)
10     {
11         ans = 0;
12         scanf("%lld %lld %lld %lld %lld", &p, &a, &b, &x1, &t);
13         while(233)
14         {
15             ++ans;
16             if(ans > p)
17             {
18                 puts("-1");
19                 break;
20             }
21             if(x1 == t)
22             {
23                 printf("%lld\n", ans);
24                 break;
25             }
26             x1 = (x1 * a + b) % p;
27         }
28     }
29     return 0;
30 }
View Code

考虑展开式子。

由$x[i$ $+$ $1]$ $≡$ $a$ $*$ $x[i]$ $+$ $b$ $(mod$ $p)$得

$x[1]$ $=$ $x1$ $\%$ $p;$

$x[2]$ $=$ $(a$ $*$ $x1$ $+$ $b)$ $\%$ $p;$

$x[3]$ $=$ $(a$2 $*$ $x1$ $+$ $a$ $*$ $b$ $+$ $b)$ $\%$ $p;$

$x[4]$ $=$ $(a$3 $*$ $x1$ $+$ $a$2 $*$ $b$ $+$ $a$ $*$ $b$ $+$ $b)$ $\%$ $p;$

$x[5]$ $=$ $(a$4 $*$ $x1$ $+$ $a$3 $*$ $b$ $+$ $a$2 $*$ $b$ $+$ $a$ $*$ $b$ $+$ $b)$ $\%$ $p;$

$……$

$x[k]$ $=$ $(a$k - 1 $*$ $x1$ $+$ $a$k - 2 $*$ $b$ $+$ $a$k - 3 $*$ $b$ $+$ $......$ $+$ $a$2 $*$ $b$ $+$ $a$ $*$ $b$ $+$ $b)$ $\%$ $p;$

题目中求在第几天看到了第$t$页,如果永远也看不到就输出$-1$,否则输出最早的一天。

不妨设在第$k$天看到了第$t$页,于是可得方程:

$x[k]$ $≡$ $t$ $(mod$ $p);$

$a$k - 1 $*$ $x1$ $+$ $a$k - 2 $*$ $b$ $+$ $a$k - 3 $*$ $b$ $+$ $......$ $+$ $a$2 $*$ $b$ $+$ $a$ $*$ $b$ + $b$ $≡$ $t$ $(mod$ $p);$

其中,$a、x1、b、t、p$已知,求解$k$的值。

先不看第一项$a$k - 1 $*$ $x1$,容易发现其余项是一个等比数列。

提出$b$:

$a$k - 1 $*$ $x1$ $+$ $b$ $*$ $(a$k - 2 $+$ $a$k - 3 $+$ $......$ $+$ $a$2 $+$ $a$ $+$ $1)$ $≡$ $t$ $(mod$ $p);$

求解$a$k - 2 $+$ $a$k - 3 $+$ $......$ $+$ $a$2 $+$ $a$ $+$ $1$的值可以代入等比数列求和公式

解得:

当$a$ $=$ $1$时,原式 $=$ $k$ $-$ $1$;

当$a$ $≠$ $1$时,原式 $=$ $(1$ $-$ $a$k - 1) $/$ $(1$ $-$ $a);$

代入原方程可得:

当$a$ $=$ $1$时,原方程为:

$x1$ $+$ $b$ $*$ $(k$ $-$ $1)$ $≡$ $t$ $(mod$ $p);$

$x1$ $+$ $bk$ $-$ $b$ $≡$ $t$ $(mod$ $p);$

$bk$ $≡$ $t$ $+$ $b$ - $x1$ $(mod$ $p);$

当$a$ $≠$ $1$时,原方程为:

$a$k - 1 $*$ $x1$ $+$ $b$ $*$ $(1$ $-$ $a$k - 1$)$ $/$ $(1$ $-$ $a)$ $≡$ $t$ $(mod$ $p);$

$a$k - 1 $*$ $x1$ $*$ $(1$ $-$ $a)$ $+$ $b$ $*$ $(1$ $-$ $a$k - 1$)$ $≡$ $t$ $*$ $(1$ $-$ $a)$ $(mod$ $p);$

$a$k - 1 $*$ $(x1$ $-$ $x1$ $*$ $a)$ $+$ $b$ $-$ $b$ $*$ $a$k - 1 $≡$ $t$ $-$ $t$ $*$ $a$ $(mod$ $p);$

$a$k - 1 $*$ $(x1$ $-$ $x1$ $*$ $a$ $-$ $b)$ $≡$ $t$ $-$ $t$ $*$ $a$ $-$ $b$ $(mod$ $p);$

$a$k - 1 $≡$ $(t$ $-$ $t$ $*$ $a$ $-$ $b)$ $*$ $(x1$ $-$ $x1$ $*$ $a$ $-$ $b)$-1 $(mod$ $p);$(这里的$-1$次方表示逆元)

整理得:

当$a$ $=$ $1$时,$bk$ $≡$ $t$ $+$ $b$ $-$ $x1$ $(mod$ $p);$

当$a$ $≠$ $1$时,$a$k - 1 $≡$ $(t$ $-$ $t$ $*$ $a$ $-$ $b)$ $*$ $(x1$ $-$ $x1$ $*$ $a$ $-$ $b)$-1 $(mod$ $p);$

易看出:

当$a$ $=$ $1$时,可以用$exgcd$求解出$k$;

当$a$ $≠$ $1$时,可以用$BSGS$求解出$k$;

有一些需要特判的坑点在代码里:

 1 #include <bits/stdc++.h>
 2 #define INF 0x3f3f3f3f
 3 #define ll long long 
 4 using namespace std;
 5 int T;
 6 ll a, b, p, x, y, ps, d, x1, t;
 7 map < ll, ll > mp;
 8 void exgcd(ll a, ll b, ll &x, ll &y)
 9 {
10     if(!b) x = 1, y = 0, d = a;
11     else exgcd(b, a % b, y, x), y -= x * (a / b);
12     return;
13 }
14 ll ksm(ll x, ll y, ll mod)
15 {
16     ll ans = 1;
17     for(; y; y >>= 1)
18     {
19         if(y & 1) ans = ans * x % mod;
20         x = x * x % mod;
21     }
22     return ans;
23 }
24 ll bsgs(ll a, ll b, ll p)
25 {
26     mp.clear();//记得清空 
27     b = (b % p + p) % p;//b可能是负数 
28     if(a == b) return 1;//特判a == b 
29     ps = ceil((double)sqrt(p));
30     exgcd(ksm(a, ps, p), p, x, y);
31     x = (x % p + p) % p;
32     mp[1] = 0;
33     for(int i = 1, t = a; i <= ps; ++i, t = t * a % p) if(!mp[t]) mp[t] = i;//这里要求先输出最先读到的那一天所以要判空 
34     for(int i = 0, xx = 1; i <= ps; ++i, xx = xx * x % p)
35     {
36         if(b * xx % p == 1) return ps * i;//这里由于我前面设了mp[1] = 0所以要特判一下 
37         if(mp[b * xx % p]) return ps * i + mp[b * xx % p];
38     }
39     return -1;
40 }
41 int main()
42 {
43     scanf("%d", &T);
44     while(T--)
45     {
46         scanf("%lld %lld %lld %lld %lld", &p, &a, &b, &x1, &t);
47         if(t == x1)//特判 
48         {
49             puts("1");
50             continue;
51         }
52         if(a == 0)//特判 
53         {
54             if(t == b) puts("2");
55             else puts("-1");
56             continue;
57         }
58         if(a == 1)//exgcd求 
59         {
60             ll c = t + b - x1;
61             c = (c % p + p) % p;//先取模 
62             if(!c)//c = 0直接输出p就是了,具体为什么自己把c = 0的式子带进去推 
63             {
64                 printf("%lld\n", p);
65                 continue;
66             } 
67             exgcd(b, p, x, y);
68             x =    (x % p + p) % p;
69             if(c % d != 0) puts("-1");
70             else printf("%lld\n", (x * c / d % p + p) % p);
71             continue;
72         }//bsgs求 
73         exgcd(((x1 - x1 * a - b) % p + p) % p, p, x, y);
74         x = (x % p + p) % p;
75         ll tt = bsgs(a, t * x - t * a % p * x - b * x, p);//三连乘别忘了取模 
76         if(tt != -1) printf("%lld\n", tt + 1);
77         else puts("-1");
78     }
79     return 0;
80 }

 调试代码所用的bat:

:begin
maker.exe > P3306.in
随机数生成器.exe < P3306.in > P3306.out
brute.exe < P3306.in > P3306.ans
fc P3306.out P3306.ans
if not errorlevel = 1 goto begin
pause

brute就是上面的暴力,maker就是我自己写的一个数据生成器。

posted @ 2020-08-16 21:32  louis_11  阅读(169)  评论(0编辑  收藏  举报