题意:给出一个字符串,每次询问给出x和y要求算出从x到y的每个字符的(ASCII 码值-28)的值的积(mod9973)。
分析:首先的想法肯定是算出每个位置的前缀积,然后只要F[y]/F[x-1]即可。但是每个前缀积都已经mod9973了,就不能直接这样得出结果了,所以利用求逆元。因为a/b(mod p)p是质数的话,相当于a*inv(b) (mod p),所以只要保存每个位置的前缀积和前缀积的逆元就可以了。
但是题目有个坑点,如果x和y超出了这次字符串的位置,可以使用上一次数据储存的值而不是0,因为题目给定了x和y是不会超过字符串的长度的,所以每次给出输入数据以后我都memset一遍,导致这个地方WA了好几次。
同时,这里有个技巧就是,既然逆元是mod9973的,那么只要储存下0到9972的逆元做个预处理即可节省大量的时间。具体见代码:
1 #include <stdio.h> 2 #include <algorithm> 3 #include <string.h> 4 using namespace std; 5 const int mod = 9973; 6 int inv(int a,int b) //b是mod-2 7 { 8 int ans=1; 9 while(b) 10 { 11 if(b&1) ans=(ans*a)%mod; 12 b>>=1; 13 a=a*a%mod; 14 } 15 return ans; 16 } 17 char s[100000+10]; 18 int num[100000+10]; 19 int p[100000+10]; 20 int res[10000]; 21 int main() 22 { 23 int T; 24 for(int i=1;i<mod;i++) res[i]=inv(i,mod-2); 25 while(scanf("%d",&T)==1) 26 { 27 scanf("%s",s+1); 28 int len =strlen(s+1); 29 //memset(num,0,sizeof(num)); 30 //memset(p,0,sizeof(p)); 31 num[0]=p[0]=1; 32 for(int i=1;i<=len;i++) 33 { 34 num[i]=num[i-1]*(s[i]-28)%mod; 35 p[i]=res[num[i]]; 36 } 37 while(T--) 38 { 39 int x,y; 40 scanf("%d%d",&x,&y); 41 printf("%d ",num[y]*p[x-1]%mod); 42 } 43 } 44 return 0; 45 }
注意红色部分的代码即可。