【逆元】

Problem A

   
 Accepts: 599
   
 Submissions: 5110
 Time Limit: 2000/1000 MS (Java/Others)
   
 Memory Limit: 65536/65536 K (Java/Others)
Problem Description

度熊手上有一本字典存储了大量的单词,有一次,他把所有单词组成了一个很长很长的字符串。现在麻烦来了,他忘记了原来的字符串都是什么,神奇的是他竟然记得原来那些字符串的哈希值。一个字符串的哈希值,由以下公式计算得到:

H(s)=\prod_{i=1}^{i\leq len(s)}(S_{i}-28)\ (mod\ 9973)H(s)=i=1ilen(s)(Si28) (mod 9973)

S_{i}Si代表 S[i] 字符的 ASCII 码。

请帮助度熊计算大字符串中任意一段的哈希值是多少。

Input

多组测试数据,每组测试数据第一行是一个正整数NN,代表询问的次数,第二行一个字符串,代表题目中的大字符串,接下来NN行,每行包含两个正整数aabb,代表询问的起始位置以及终止位置。

1\leq N\leq 1,0001N1,000

1\leq len(string)\leq 100,0001len(string)100,000

1\leq a,b\leq len(string)1a,blen(string)

Output

对于每一个询问,输出一个整数值,代表大字符串从 aa 位到 bb 位的子串的哈希值。

Sample Input
2
ACMlove2015
1 11
8 10
1
testMessage
1 1
Sample Output
6891
9240

88

想A这道题 一定要懂得逆元  

比如i*j%9973=1  那么j就是i的逆元

有两种方法求逆元:

  1. 对字符串s,从左到右,对每个字串存储其哈希值dp。如果只考虑乘法的消耗,这一步的时间复杂度是O(len(s))。

  2. 对于任一子串,子串 S(a...b)的 hash 值: H(s)=dp[b]/dp[a−1]%mod

  3. 1/dp[a−1]%9973就是dp[a-1]的逆元。求逆元的时间复杂度是O(log(9973))

1,、就是对所有逆元打表  怎么求逆元呢?  看下图  如果你想问怎么来的 请百度。。。

取模下的乘法  就是乘上它的逆元


2、dp[b] / dp[a-1] % mod = (dp[b] * (dp[a-1] ^ (mod-2) % mod))% mod;用快速幂求;

代码如下:

#include<stdio.h>  
#include<string.h>  
#include<string>  
#include<iostream>  
#include<algorithm>  
using namespace std;  
#define LL long long  
const LL maxm=1e5+10;  
const LL mod=9973;  
string s;  
LL dp[maxm];  
LL quickmod(LL a,LL b)  
{  
    LL sum=1;  
    while(b)  
    {  
        if(b&1)  
            sum=(sum*a)%mod;  
        b>>=1;  
        a=(a*a)%mod;  
    }  
    return sum;  
}  
int main()  
{  
    LL n;  
    while(scanf("%I64d",&n)!=EOF)  
    {  
       cin>>s;  
        dp[0]=1;  
        for(LL i=1; i<=s.length(); i++)  
        {  
            dp[i]=(dp[i-1]*(s[i-1]-28))%mod;  
        }  
       LL l,r;  
        for(LL i=0; i<n; i++)  
        {  
            scanf("%I64d%I64d",&l,&r);  
            if(l>r)  
            {  
                swap(l,r);  
            }  
            printf("%I64d\n",(dp[r]*(quickmod(dp[l-1],mod-2)%mod))%mod);  
        }  
    }  
    return 0;  


代码2;

#include<iostream>
#include<cstring>
#include<cstdio>
#define LL __int64
#define mod 9973
using namespace std;
LL inv[100010]={0};
char s[100100];
LL H[100100];
//  计算逆元方法一:
LL mod_pow(LL x, LL n) {
    LL res = 1;
    while(n) {
        if(n & 1) res = res * x % mod;
        x = x * x % mod;
        n >>= 1;
    }
    return res%mod;
}
int main()
{
    inv[1]=1;
    inv[0]=1;
    //  计算逆元方法二:
    for(int i=2;i<mod+1;i++)
   {
        inv[i]=(mod-mod/i)*inv[mod%i]%mod;
    }
    int n,l,r;
    H[0]=1;
    while(~scanf("%d",&n)){
        scanf("%s",s);
        int len=strlen(s);
        for(int i=1;i<=len;i++)       // 定义变量获取长度降低时间;
            H[i]=H[i-1]*(s[i-1]-28)%mod;
        while(n--){
            scanf("%d%d",&l,&r);
            if(r<l) swap(l,r);
            //printf("%I64d\n",H[r]*mod_pow(H[l-1],mod-2)%mod);
            printf("%d\n",H[r]*inv[H[l-1]]%mod);
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值