zoukankan      html  css  js  c++  java
  • 【bzoj3122】[Sdoi2013]随机数生成器 BSGS思想的利用

    题目描述

    给出递推公式 $x_{i+1}=(ax_i+b)mod p$ 中的 $p$、$a$、$b$、$x_1$ ,其中 $p$ 是质数。输入 $t$ ,求最小的 $n$ ,使得 $x_n=t$ 。若不存在则输出-1。

    输入

    输入含有多组数据,第一行一个正整数 T ,表示这个测试点内的数据组数。  
    接下来 T 行,每行有五个整数 p,a,b,X1,t ,表示一组数据。保证 X1 和 t 都是合法的页码。 

    注意: p 一定为质数

    输出

    共T行,每行一个整数表示他最早读到第t页是哪一天。如果他永远不会读到第t页,输出-1。 

    样例输入

    3
    7 1 1 3 3
    7 2 2 2 0
    7 2 2 2 1

    样例输出

    1
    3
    -1


    题解

    BSGS思想的利用

    (以下为思考过程)

    早就知道网上的大部分题解:把通项公式变形为$x_{i+1}+c=a(x_i+c)mod p$,然后转化为BSGS的经典方程$a^xmod p=c$来求解。

    然而这样需要exgcd之类的,还需要乱七八糟的特判,代码少说也有80+行。

    考虑:直接利用BSGS的meet-in-the-middle思想,而不是无脑套方程求解。

    (以上为思考过程)

    为了方便,设$f(x)=(ax+b)mod p$,$f^d(x)$表示$f(x)$的$d$次复合,即$f(f(...(x)))$,其中$f$的个数为$d$。

    我们要求的就是满足$f^n(x_1)=t$的最小的$n$。

    首先根据抽屉(鸽笼)原理,如果有解则一定不超过$p$。

    把答案$n$写成$km-l$的形式,其中$m=lceilsqrt n ceil$。

    那么我们要求的就是$f^{km-l}(x_1)=t$。

    我们把这个式子左右同时复合$l$层,变为$f^{km}(x_1)=f^l(t)$。

    由于一次函数复合以后还是一次函数,因此可以直接处理出$f^l(x)$和$f^{km}(x)$中一次项和常数项的系数。具体方法:$a(cx+d)+b=acx+ad+b$。

    然后把所有的$f^{km}(x_1)$按照处理出的系数算出来,放到哈希表中(代码中使用了map),其中值相同的只取$k$较小的。

    再枚举$l$,按照处理出的系数算出$f^l(t)$,看哈希表中是否有这个数即可,如果有则用$km-l$更新答案。

    思考一下为什么一般情况下这样是对的:因为$p$是足够大的质数,因此所有数在模意义下都是存在的,并且存在唯一的逆。所以对于已知的$(cx+d)mod p=e$,知道$e$可以直接推出$x$。

    这个做法当$a=0$时不成立,因为不存在唯一的逆。因此需要特殊处理。

    时间复杂度$O(sqrt nlog n)$

    代码只有40行左右^_^

    #include <map>
    #include <cmath>
    #include <cstdio>
    using namespace std;
    typedef long long ll;
    map<ll , ll> mp;
    map<ll , ll>::iterator it;
    ll u[50010] , v[50010];
    int main()
    {
    	int T;
    	scanf("%d" , &T);
    	while(T -- )
    	{
    		ll p , a , b , x , t , m , i , ans = 1ll << 62;
    		scanf("%lld%lld%lld%lld%lld" , &p , &a , &b , &x , &t);
    		if(!a)
    		{
    			if(t == x) puts("1");
    			else if(t == b) puts("2");
    			else puts("-1");
    		}
    		else
    		{
    			mp.clear() , m = ceil(sqrt(p));
    			u[0] = 1 , v[0] = 0;
    			for(i = 1 ; i <= m ; i ++ ) u[i] = u[i - 1] * a % p , v[i] = (a * v[i - 1] + b) % p;
    			for(i = 1 ; i <= m ; i ++ )
    			{
    				x = (x * u[m] + v[m]) % p;
    				if((it = mp.find(x)) == mp.end())
    					mp[x] = i;
    			}
    			for(i = 1 ; i <= m ; i ++ )
    				if((it = mp.find((t * u[i] + v[i]) % p)) != mp.end())
    					ans = min(ans , it->second * m - i + 1);
    			if(ans == 1ll << 62) puts("-1");
    			else printf("%lld
    " , ans);
    		}
    	}
    	return 0;
    }
    

     

  • 相关阅读:
    Java内存分析工具MAT
    jvisualvm安装Visual GC插件
    Jmeter取样器之JDBC Request
    tomcat监控页面
    高并发的参数优化(Tomcat、数据库、linux服务器)
    UITableView自定义Section
    iPhone的动画效果类型及实现方法
    自定义UITableViewCell详细步骤
    扩展NSDate类(NSDateHelper)
    UITableView实现Cell的滑动删除
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/7763526.html
Copyright © 2011-2022 走看看