poj1845 Sumdiv
poj1845 Sumdiv 数学题
令人痛苦van分的数学题!
题意:求a^b的所有约数(包括1和它本身)之和%9901
这怎么做呀!!!
百度:约数和定理,会发现 p1^a1 * p2^a2 * ... * pn^an
这个数的约数和是:(1 + p1 + p1^2 + ... + p1^a1) * (1 + p2 + ... + p2^a2) * ... * (1 + pn + ... + pn^an)
证明:由乘法原理可直接证明
然后我们对于a^b
运用这个公式即可。
那么对于 (1 + pi + ... + pi^ai)
我们难道要暴力求吗?
不,这显然是个等比数列!我们用公式!
那么我们就成功WA了!
…查找原因,发现q-1
可能是9901
的整数倍,导致无法求逆元。
那么我们退而求次,用递归求和!
a1 + a1*q + a1*q^2 + ... + a1*q^n = (q^(n/2) + 1) * (a1 + a1*q + ... + a1*q^(n/2))
把上面那个式子分n的奇偶讨论一下就行了。
(法②:当q-1为9901的整数倍时,q % 9901 = 1,又因为a1 = 1,上式显然为n + 1)
然后我们开开心心的调了一年之后WA了….
查找原因:没开long long导致qpow爆了
然后就A了!
1 //poj1845 sumdiv 2 #include <cstdio> 3 using namespace std; 4 const int N = 50000,mo = 9901; 5 int p[N][2],P; 6 int qpow(int a,int b) { 7 int ans=1; 8 while(b) { 9 if(b&1) { 10 ans=ans*a%mo; 11 } 12 a=a*a%mo; 13 b=b>>1; 14 } 15 return ans; 16 } 17 18 void split(int a,int b) { 19 for(int i=2; a>1; i++) { 20 if(!(a%i)) { 21 p[++P][0] = i; 22 } 23 while(!(a%i)) { 24 a/=i; 25 p[P][1]++; 26 } 27 } 28 for(int i=1; i<=P; i++) { 29 p[i][1]*=b; 30 } 31 return; 32 } 33 34 long long getQsum(int a1,int q,int n) { 35 if(q==1) { 36 return a1*n%mo; 37 } 38 if(n==1) { 39 return a1; 40 } 41 if(n&1) { //奇数 42 long long ans = ((1+qpow(q,n>>1))*getQsum(a1,q,n>>1)+(a1*qpow(q,n-1)))%mo; 43 return ans; 44 } 45 //偶数 46 long long ans = ((1+qpow(q,n>>1))*(getQsum(a1,q,n>>1)))%mo; 47 return ans; 48 } 49 50 void solve() { 51 long long ans=1; 52 for(int i=1; i<=P; i++) { 53 int temp=getQsum(1,p[i][0],p[i][1]+1); 54 ans*=temp; 55 ans%=mo; 56 } 57 printf("%I64d",ans); 58 return; 59 } 60 61 int main() { 62 //freopen("in.in","r",stdin); 63 //freopen("my.out","w",stdout); 64 int a,b; 65 scanf("%d%d",&a,&b); 66 if(!a) { 67 printf("0"); 68 return 0; 69 } 70 split(a,b); 71 solve(); 72 return 0; 73 }
我们学到了什么?
我们学到了什么?
- 约数和定理
- How to 求逆元
- How to 递归求等比数列和
- 开long long!!!!
————————end————————