Astar2016-Round1 Problem A(前缀积+乘法逆元+快速幂取余)
2016-05-16 12:37
316 查看
FROM:
2016"百度之星" - 资格赛(Astar Round1)
http://bestcoder.hdu.edu.cn/contests/contest_showproblem.php?cid=690&pid=1001
Problem Description
度熊手上有一本字典存储了大量的单词,有一次,他把所有单词组成了一个很长很长的字符串。现在麻烦来了,他忘记了原来的字符串都是什么,神奇的是他竟然记得原来那些字符串的哈希值。一个字符串的哈希值,由以下公式计算得到:(公式略,为连乘求模) 请帮助度熊计算大字符串中任意一段的哈希值是多少。
Input
多组测试数据,每组测试数据第一行是一个正整数NN,代表询问的次数,第二行一个字符串,代表题目中的大字符串,接下来NN行,每行包含两个正整数aa和bb,代表询问的起始位置以及终止位置。
1≤N≤1,000
1≤len(string)≤100,000
1≤a,b≤len(string)
Output
对于每一个询问,输出一个整数值,代表大字符串从 aa 位到 bb 位的子串的哈希值。
Sample Input
Copy
2
ACMlove2015
1 11
8 10
1
testMessage
1 1
Sample Output
6891
9240
88
分析
我AC这题的代码采用的是线段树,但看到别人用的方法更好,这边介绍别人方法。
首先是前缀积:
为何可以用逆元 http://blog.csdn.net/cqlf__/article/details/7953039 摘取博客部分:
比如: (8/2)%5 我们求a*b*c*d*e*f*g..../z 前面乘积部分LL存不下所以要一边mod一边乘。最后处理到除z时,不一定能除尽。比如前面那个例子,8%5=3,3除不尽2就乘以2%5的逆元在%5
2%5的逆元=2^(5-2)=8 这是计算逆元的一种方法,后面讲。还有一直哦你方法是扩展欧几里德算法也是后面详细讲。
(3*8)%5=4=4%5
===============
在计算(a/b)%Mod时,往往需要先计算b%Mod的逆元p(b有逆元的条件是gcd(b,Mod)==1,显然素数肯定有逆元),然后由(a*p)%Mod得结果c。这里b的逆元p满足(b*p)%Mod=1。先来简单证明一下:
(a/b)%Mod=c; (b*p)%Mod=1; ==》 (a/b)*(b*p) %Mod=c; ==》 (a*p)%Mod=c;
从上面可以看出结论的正确性,当然这里b需要是a的因子。接下来就需要知道根据b和Mod,我们怎么计算逆元p了。扩展欧几里德算法,大家应该都知道,就是已知a、b,求一组解(x,y)使得a*x+b*y=1。这里求得的x即为a%b的逆元,y为b%a的逆元(想想为什么?把方程两边都模上b或a看看)。调用ExtGcd(b,Mod,x,y),x即为b%Mod的逆元p。
求b%Mod的逆元p还有另外一种方法,即p=b^(Mod-2)%Mod,因为b^(Mod-1)%Mod=1(这里需要Mod为素数)。
逆元详解 http://blog.csdn.net/acdreamers/article/details/8220787
重点(1/a)(mod m)=a^(m-2)。注意要像本题中这样使用逆元,要满足素数条件。
对于a*b*c/(x*y*z),等于(a*x的逆元)*(b*y的逆元)*(c*z的逆元),求大数组合的时候可以利用这点。
最后是快速幂取模:
原理为:
a为底,b为幂
可以把b按二进制展开为:b = p(n)*2^n + p(n-1)*2^(n-1) +…+ p(1)*2 + p(0)
其中p(i) (0<=i<=n)为 0 或 1
这样 a^b = a^ (p(n)*2^n + p(n-1)*2^(n-1) +...+ p(1)*2 + p(0))
= a^(p(n)*2^n) * a^(p(n-1)*2^(n-1)) *...* a^(p(1)*2)*a^p(0)
另一种求逆元方法:http://www.w2bc.com/article/137005
代码
2016"百度之星" - 资格赛(Astar Round1)
http://bestcoder.hdu.edu.cn/contests/contest_showproblem.php?cid=690&pid=1001
Problem Description
度熊手上有一本字典存储了大量的单词,有一次,他把所有单词组成了一个很长很长的字符串。现在麻烦来了,他忘记了原来的字符串都是什么,神奇的是他竟然记得原来那些字符串的哈希值。一个字符串的哈希值,由以下公式计算得到:(公式略,为连乘求模) 请帮助度熊计算大字符串中任意一段的哈希值是多少。
Input
多组测试数据,每组测试数据第一行是一个正整数NN,代表询问的次数,第二行一个字符串,代表题目中的大字符串,接下来NN行,每行包含两个正整数aa和bb,代表询问的起始位置以及终止位置。
1≤N≤1,000
1≤len(string)≤100,000
1≤a,b≤len(string)
Output
对于每一个询问,输出一个整数值,代表大字符串从 aa 位到 bb 位的子串的哈希值。
Sample Input
Copy
2
ACMlove2015
1 11
8 10
1
testMessage
1 1
Sample Output
6891
9240
88
分析
我AC这题的代码采用的是线段树,但看到别人用的方法更好,这边介绍别人方法。
首先是前缀积:
H[0] = 1; for(int i = 1 ;i<=len;i++) { H[i]=H[i-1]*(Hstr[i-1]-28)%mods; }然后是逆元:
为何可以用逆元 http://blog.csdn.net/cqlf__/article/details/7953039 摘取博客部分:
比如: (8/2)%5 我们求a*b*c*d*e*f*g..../z 前面乘积部分LL存不下所以要一边mod一边乘。最后处理到除z时,不一定能除尽。比如前面那个例子,8%5=3,3除不尽2就乘以2%5的逆元在%5
2%5的逆元=2^(5-2)=8 这是计算逆元的一种方法,后面讲。还有一直哦你方法是扩展欧几里德算法也是后面详细讲。
(3*8)%5=4=4%5
===============
在计算(a/b)%Mod时,往往需要先计算b%Mod的逆元p(b有逆元的条件是gcd(b,Mod)==1,显然素数肯定有逆元),然后由(a*p)%Mod得结果c。这里b的逆元p满足(b*p)%Mod=1。先来简单证明一下:
(a/b)%Mod=c; (b*p)%Mod=1; ==》 (a/b)*(b*p) %Mod=c; ==》 (a*p)%Mod=c;
从上面可以看出结论的正确性,当然这里b需要是a的因子。接下来就需要知道根据b和Mod,我们怎么计算逆元p了。扩展欧几里德算法,大家应该都知道,就是已知a、b,求一组解(x,y)使得a*x+b*y=1。这里求得的x即为a%b的逆元,y为b%a的逆元(想想为什么?把方程两边都模上b或a看看)。调用ExtGcd(b,Mod,x,y),x即为b%Mod的逆元p。
求b%Mod的逆元p还有另外一种方法,即p=b^(Mod-2)%Mod,因为b^(Mod-1)%Mod=1(这里需要Mod为素数)。
逆元详解 http://blog.csdn.net/acdreamers/article/details/8220787
重点(1/a)(mod m)=a^(m-2)。注意要像本题中这样使用逆元,要满足素数条件。
对于a*b*c/(x*y*z),等于(a*x的逆元)*(b*y的逆元)*(c*z的逆元),求大数组合的时候可以利用这点。
最后是快速幂取模:
原理为:
a为底,b为幂
可以把b按二进制展开为:b = p(n)*2^n + p(n-1)*2^(n-1) +…+ p(1)*2 + p(0)
其中p(i) (0<=i<=n)为 0 或 1
这样 a^b = a^ (p(n)*2^n + p(n-1)*2^(n-1) +...+ p(1)*2 + p(0))
= a^(p(n)*2^n) * a^(p(n-1)*2^(n-1)) *...* a^(p(1)*2)*a^p(0)
LL mod_pow(LL x,LL n,LL mod) { // x是底数,n是幂数,mod是取余数 LL res = 1; x%=mod; while(n>0){ if(n & 1){ res = res * x % mod; } x = x * x % mod; n>>=1; } return res; }
另一种求逆元方法:http://www.w2bc.com/article/137005
inv[1] = 1; for (int i = 2; i<MAXN; i++) inv[i] = inv[MOD%i]*(MOD-MOD/i)%MOD;
代码
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> using namespace std; const int MAXN = 1e5 + 5; int H[MAXN]; char Hstr[MAXN]; int N,l,r; const int mods = 9973; typedef long long LL; //快速幂取余 LL mod_pow(LL x,LL n,LL mod) { LL res = 1; //x%=mod; while(n>0){ if(n & 1){ res = res * x % mod; } x = x * x % mod; n>>=1; } return res; } int main () { freopen("in.txt","r",stdin); while(scanf("%d",&N)!=EOF){ scanf("%s",Hstr); int len =strlen(Hstr); H[0] = 1; for(int i = 1 ;i<=len;i++){ H[i]=H[i-1]*(Hstr[i-1]-28)%mods; } while(N--){ scanf("%d%d",&l,&r); printf("%I64d\n",(LL)H[r]*mod_pow(H[l-1],mods-2,mods)%mods); } } }
相关文章推荐
- 25条提高iOS App性能的技巧和诀窍
- 元素多层嵌套,JS获取问题
- mac Tomcat启动
- REST风格的webservice设计模式
- 第10、11周项目-摩托车继承自行车和机动车
- UVALive - 4264 Message (模拟)
- vs2013下编写你的第一个CUDA程序
- Perl printf 函数
- cv::Mat& dst 这名话从语法是怎么理解?
- linux路由表,策略路由,路由查找
- 字符串混淆技术应用 设计一个字符串混淆程序 可混淆.NET程序集中的字符串
- 二叉树的建树,按层遍历,结点总数,页结点,深度以及三序非递归遍历二叉树,建立中序线索二叉树
- 第十一周项目——Time类中的运算符重载
- js中使用for 循环和 for in 遍历数组区别
- IOS 数据解析
- css图片变色变暗变亮
- css图片变色变暗变亮
- Yii2中OAuth扩展及QQ互联登录实现方法
- 字符串混淆技术在.NET程序保护中的应用及如何解密被混淆的字符串
- Sublime Text2插件之 - JSON格式化