[BZOJ3122][Sdoi2013]随机数生成器(快速幂+BSGS)
2016-03-02 19:14
387 查看
题目描述
传送门题解
先说一下暴力的写法,这道题最好是写一个小暴力拍一下:按照题目描述的枚举即可,但是要注意判断无解的条件。通过观察可以发现这个数列是存在循环节的,如果已经找到了循环节并且第一个循环节内没有满足条件的xn那么就可以直接退出啦。
正解的话应该是把这个式子变一下形然后做BSGS,推倒如下:
原式可以化为:Xi+1≡aXi+b(modp)
Xi+1+ba−1≡aXi+b+ba−1(modp)
Xi+1+ba−1≡a(Xi+ba−1)(modp)
则
Xn+ba−1≡a(Xn−1+ba−1)(modp)……①
Xn−1+ba−1≡a(Xn−2+ba−1)(modp)……②
讲②代人①得到
Xn+ba−1≡a2(Xn−2+ba−1)(modp)
很神奇啊对不对,迭代下去
于是我们可以得到:
Xn+ba−1≡an−1(X1+ba−1)(modp)
我们发现,除了an−1之外,其余各项都是常数,于是
an−1≡(Xn+b∗inv(a−1))∗inv(X1+b∗inv(a−1))(modp)
其中inv为某数的逆元。
用BSGS算法求n-1即可
需要注意的一些小问题:
①在同余式中除以一个数等于乘以这个数的逆元。在模p意义下a的逆元为ap−2,这个可以通过费马小定理证明。
②因为满足0<=a,b,x1<=p-1,且p为质数,那么一定满足a-1和p互质,那么我们就可以用快速幂求a-1的逆元啦。
③一些特殊情况需要特判
代码
#include<iostream> #include<cstring> #include<cstdio> #include<cmath> #include<map> using namespace std; #define LL long long LL T,P,a,b,x1,t; LL ny1,ny2,val1,val2,val3; LL ans; map <LL,LL> hash; inline LL fast_pow(LL a,LL p){ LL ans=1; for (;p;p>>=1,a=a*a%P) if (p&1) ans=ans*a%P; return ans; } inline LL BSGS(LL a,LL b){ LL m=ceil(sqrt(P)); LL a_m=fast_pow(a,m); hash.clear(); LL mul=1; LL val=mul*b%P; hash[val]=0; for (LL j=1;j<=m;++j){ mul=mul*a%P; val=mul*b%P; hash[val]=j; } mul=1; for (LL i=1;i<=m;++i){ mul=mul*a_m%P; if (hash[mul]){ LL x=i*m-hash[mul]; return x+1; } } return -1; } int main(){ scanf("%lld",&T); while (T--){ scanf("%lld%lld%lld%lld%lld",&P,&a,&b,&x1,&t); //一坨特判 if (t==x1){ printf("1\n"); continue; } if (a==0){ if (t==b) printf("2\n"); else printf("-1\n"); continue; } if (a==1&&b==0){ printf("-1\n"); continue; } if (a==1){ int nyb=fast_pow(b,P-2); ans=((((t-x1)%P+P)%P)*nyb%P)%P; printf("%lld\n",ans+1); continue; } ny1=fast_pow(a-1,P-2); val1=b*ny1%P; val2=(x1%P+val1)%P; ny2=fast_pow(val2,P-2); val3=(t+val1)%P; b=(val3*ny2)%P; ans=BSGS(a,b); printf("%lld\n",ans); } }
总结
①注意负数的情况。②先%后+还是先+后%想清楚。在纸上列列公式,实在不行先换元。
③在%p意义下a的逆元为ap−2,ap互质且p为质数,不要搞错了。
④其实这道题是在式子的两边同时加上了一个常数,然后化成通项公式。也就是找出X1和Xn的关系。这样的话要保证X1和Xn的系数相等(把a提出),且可以一直推下去(也就是说保证式子左边的一坨和右边除去系数的一坨相同,则同时加上的常数可以计算出来)。
相关文章推荐
- Android之定时器实现的几种方式和removeCallbacks失效问题详解
- Android性能优化之使用线程池处理异步任务
- C/C++ 文件路径解析
- ORA-01155: the database is being opened, closed, mounted or dismounted
- iOS开发实战之app获取通讯录(iOS 9)
- List集合去除重复对象
- C/C++ 文件路径解析
- [iOS学习]Xcode插件的使用
- BigDecimal的equals
- HDU 3308 LCIS
- TIMUS-1748. The Most Complex Number-求反素数(数学+搜索)
- HDU 1003 Max Sum【最大连续子序列】
- IE8 jquery ajax获取静态资源报错TypeError 拒绝访问
- spark 各组件研究
- Eclipse中如何开启Assert断言
- 考研啦 博客更新的少啦
- 安装Linux后修改grub默认启动选项
- android包名
- C# 获取mp3文件的歌曲时间长度
- bzoj 2542: [Ctsc2001]终极情报网 费用流