HDU - 5685 Problem A(逆元)
这题我第一次想的就是直接模拟,因为我是这样感觉的,输入n是3次方,长度是5次方,加起来才8次方,里面的操作又不复杂,感觉应该能过,然而不如我所料,TLE了,玛德,这是第一次的代码。
#include <bits/stdc++.h> using namespace std; const int INF=0x3f3f3f3f; typedef long long LL; #define PI(A) printf("%d\n",A) #define SI(N) scanf("%d",&(N)) #define SII(N,M) scanf("%d%d",&(N),&(M)) #define cle(a,val) memset(a,(val),sizeof(a)) #define rep(i,b) for(int i=0;i<(b);i++) #define Rep(i,a,b) for(int i=(a);i<=(b);i++) #define reRep(i,a,b) for(int i=(a);i>=(b);i--) const double EPS= 1e-9 ; /* ///////////////////////// C o d i n g S p a c e ///////////////////////// */ const int MAXN= 100000 + 5 ; char str[MAXN]; int N; int main() { while(~SI(N)) { int x,y; scanf("%s",str); while(N--) { LL ans=1; SII(x,y); x--,y--; for (int i=x;i<=y;i++) { ans=ans*((int)str[i]-28); ans%=9973; } printf("%lld\n",ans); } } return 0; }
之后想了一会,想不通,就查题解了,我看的是这个题解 http://www.cnblogs.com/inmoonlight/p/5512340.html
看了之后,觉得有几点要注意:
1.像这样求连乘的,一段区间的东西,一定要先打表,之后在输入查询,否则几乎绝对超时,比如求这题可以换成H(t)/H(s-1),由此可以想到,连加的时候也可以打表,那就是H(t)-H(s-1)
2.看到大数相除,还取模,那就是逆元了,可以用 exgcd 或 费马小定理求,这里可以写个函数自己判断下m是不是素数,9973 显然是素数,所以就费马小定理。费马小定理,H(n)的逆元为H(n)MOD-2 % MOD,当MOD是素数时。
之后,理所当然,AC
#include <bits/stdc++.h> using namespace std; const int INF=0x3f3f3f3f; typedef long long LL; #define PI(A) printf("%d\n",A) #define SI(N) scanf("%d",&(N)) #define SII(N,M) scanf("%d%d",&(N),&(M)) #define cle(a,val) memset(a,(val),sizeof(a)) #define rep(i,b) for(int i=0;i<(b);i++) #define Rep(i,a,b) for(int i=(a);i<=(b);i++) #define reRep(i,a,b) for(int i=(a);i>=(b);i--) const double EPS= 1e-9 ; /* ///////////////////////// C o d i n g S p a c e ///////////////////////// */ const int MAXN= 100000 + 5 ; char str[MAXN]; int h[MAXN]; int N; int M=9973; //快速幂模板 LL mod_pow(LL x,LL n,LL mod) { LL res=1; while(n>0){ if (n&1) res=res*x%mod; x=x*x%mod; n>>=1; } return res; } int main() { while(~SI(N)) { int x,y; scanf("%s",str); h[0]=1; //注意这是str[i]!='\0' 不是strlen(str) 如果换了 会超时,因为调用函数浪费时间,不信? 你自己试下,就知道了 for (int i=0;str[i];i++) { h[i+1]=h[i]*(str[i]-28)%M; } while(N--) { SII(x,y); printf("%lld\n",h[y]*mod_pow(h[x-1],M-2,M)%M); } } return 0; }
做完这题,有个感悟,就是不管什么题,不求速度,只求质量,一定要搞懂,就算一周只看一个题,只要搞懂了,绝对比看100道,一道都没懂好。
在附赠一个测素数的代码:
#include <bits/stdc++.h> using namespace std; int N; int main() { //PS:9973 1e9+7 都是素数 while(cin>>N) { bool fl=1; for (int i=2;i<=sqrt(N);i++) { if (N%i==0) fl=0; } puts(fl&&N>1?"yes":"no"); } return 0; }