HDU-5685-Problem A(求逆元模板题)

Problem A

Problem Description

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

H(s)=∏i≤len(s)i=1(Si−28) (mod 9973)

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

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

Input

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

1≤N≤1,000

1≤len(string)≤100,000

1≤a,b≤len(string)

Output

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

Sample Input

2
ACMlove2015
1 11
8 10
1
testMessage
1 1
 

Sample Output

6891
9240
88
 

解题思路:

维护一个前缀积,求【a-b】的哈希值。就是求pre[b]乘上pre[a-1]的逆元。

逆元的含义:

转自:https://www.cnblogs.com/zzqc/p/7192436.html
首先说明逆元的概念,类似于倒数的性质。

方程ax≡1(mod p),的解称为a关于模p的逆,当gcd(a,p)==1(即a,p互质)时,方程有唯一解,否则无解。

对于一些题目会要求把结果MOD一个数,通常是一个较大的质数,对于加减乘法通过同余定理可以直接拆开计算,

但对于(a/b)%MOD这个式子,是不可以写成(a%MOD/b%MOD)%MOD的,但是可以写为(a*b-1)%MOD,其中b-1表示b的逆元。

知道了逆元的作用,接下来就是逆元的求法。

逆元的求法:

参考: https://blog.csdn.net/u013852115/article/details/82902009

费马小定理求逆元 O(logn):


const int mod = 1000000009;
long long quickpow(long long a, long long b) {
    if (b < 0) return 0;
    long long ret = 1;
    a %= mod;
    while(b) {
        if (b & 1) ret = (ret * a) % mod;
        b >>= 1;
        a = (a * a) % mod;
    }
    return ret;
}
long long inv(long long a) {
    return quickpow(a, mod - 2);
}
 

逆元线性筛 O(n):


const int mod = 1000000009;
const int maxn = 10005;
int inv[maxn];
inv[1] = 1;
for(int i = 2; i < 10000; i++)
    inv[i] = inv[mod % i] * (mod - mod / i) % mod;
 

阶乘的逆元 O(logn+n):


inv[maxn]=mod_pow(fac[maxn],mod-2);
for(ll i=maxn-1;i>=0;i--)
    inv[i]=(inv[i+1]*(i+1))%mod;

AC代码:

#include <cstdio>
#include <cstring>
const int maxn=1e5+20, mod=9973;
char str[maxn];
int prev[maxn], n, len;
int exgcd(int a, int b, int &x, int &y){
    if (b==0){x=1; y=0; return a;}
    int gcd=exgcd(b, a%b, y, x);
    y-=(a/b)*x;
    return gcd;
}

int inv(int a, int p){
    int x, y, gcd=exgcd(a, p, x, y);
    if (gcd==1) return (x%p+p)%p;
    return -1;
}

int main(void){
    while (scanf("%d", &n)==1 && n){
        scanf("%s", str+1);
        len=strlen(str+1);

        prev[0]=1;
        for (int i=1; i<=len; i++)
            prev[i]=(prev[i-1]*(str[i]-28))%mod;
        
        int a, b;
        while (n--){
            scanf("%d%d", &a, &b);
            printf("%d\n", (prev[b]*inv(prev[a-1], mod))%mod);
        }
    }

    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值