POJ 1845 Sumdiv(逆元或者二分求解等比数列)
2017-01-05 17:25
423 查看
题目分析
本题是求AB所有因子和对9901取模后的值。我们可以将A进行拆分。A=pk11∗pk22∗pk33.......∗pknn那么可以得到AB=pk1B1∗pk2B2∗pk3B3.......∗pknBn,于是所有因子和等于:(1+p1+p21+...+pk1B1)∗(1+p2+p22+...+pk2B2)∗...∗(1+pn+p2n+...+pknBn)二分法
(1+a+a2+a3+......+an) 当n为奇数时可以化为:(1+a(n+1)/2)∗(1+a+......+a(n−1)/2),当n为偶数时可以化为:(1+an/2)∗(1+a+......+a(n−1)/2),然后递归求解即可。#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int mod = 9901; const int maxn = 1e5+100; #define LL long long LL prime[maxn], vis[maxn], tot; void init(){ //构造素数表 tot = 0; memset(vis, 0, sizeof(vis)); for(LL i = 2; i < maxn; i++) if(!vis[i]){ prime[tot++] = i; for(LL j = i*i; j < maxn; j += i) vis[j] = 1; } } LL quick_pow(LL a, LL b){ //快速幂 int ret = 1; a %= mod; while(b){ if(b&1) ret = (ret*a)%mod; b >>= 1; a = (a*a)%mod; } return ret; } LL solve(LL a, LL n){ //对于等比数列递归求解 if(n == 0) return 1; LL t = solve(a, (n-1)/2); if(n&1){ LL cur = quick_pow(a, (n+1)/2); t = (t + t*cur%mod)%mod; } else{ int cur = quick_pow(a, n/2); t = (t + t*cur%mod)%mod; t = (t + quick_pow(a, n))%mod; } return t; } int main(){ init(); LL a, b; while(scanf("%lld%lld", &a, &b) != EOF){ if(a == 0) printf("0\n"); else if(a == 1) printf("1\n"); else{ LL ans = 1; for(int i = 0; i < tot; i++) if(a%prime[i] == 0){ int cnt = 0; while(a%prime[i] == 0){ cnt++; a /= prime[i]; } ans = ans*solve(prime[i], cnt*b)%mod; } if(a != 1) ans = ans*solve(a, b)%mod; printf("%lld\n", ans); } } return 0; }
第二种就是利用逆元求解
这道题我开始一直用扩展欧几里得进行求解,一直出不来,后来发现9901虽然是个素数,但是有可能另一个数是9901的倍数,那么这样肯定是错的,因此只能使用另一种求逆元的方法,即:(a/b)modm=amod(b∗m)/b,但是更奇葩的东西发生了,因此b*m会很大,因此在求快速幂的时候会爆long long,于是需要快速乘,快速乘实际上和快速幂并没有什么区别,看一下很好理解。。#include <map> #include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define LL long long const int mod = 9901; map <LL, LL> res; LL multi(LL a, LL b, LL m){ LL ans = 0; a %= m; while(b){ if(b&1){ ans = (ans + a)%m; b--; } b >>= 1; a = (a+a)%m; } return ans; } LL quick_pow(LL a, LL b, LL M){ LL ret = 1; a %= M; while(b){ if(b&1) ret = multi(ret, a, M); b >>= 1; a = multi(a, a, M); } return ret; } void prime_factor(LL n){ LL m = sqrt(n+0.5); res.clear(); for(LL i = 2; i <= m; i++){ while(n%i == 0){ ++res[i]; n /= i; } } if(n != 1) ++res ; } int main(){ LL a, b; while(scanf("%lld%lld", &a, &b) != EOF){ if(a == 0) printf("0\n"); else if(a == 1) printf("1\n"); else{ prime_factor(a); LL ans = 1; for(map <LL, LL>::iterator it = res.begin(); it != res.end(); it++){ LL first = it->first; LL second = it->second; LL M = (LL)(first-1)*mod; ans = ans*(quick_pow(first, second*b+1, M) + M - 1)/(first-1); ans = ans%mod; } printf("%lld\n", ans); } } return 0; }
相关文章推荐
- poj 1845 Sumdiv 数论--等比数列和(逆元或者递归)
- poj 1845 Sumdiv(数论:欧拉函数+二分求等比数列前n项和+快速幂取模)
- 【POJ 1845】 Sumdiv (整数唯分+约数和公式+二分等比数列前n项和+同余)
- poj 1845 Sumdiv 素数筛+快速幂求逆元+二分乘法
- POJ 1845 Sumdiv 解题报告(二分 & 逆元)
- 【POJ 1845】 Sumdiv (整数唯分+约数和公式+二分等比数列前n项和+同余)
- [数论+二分求等比数列]POJ 1845 Sumdiv
- POJ 1845 Sumdiv (逆元 等比数列求和)
- POJ 1845 Sumdiv <数论(逆元 / 二分递归)>
- poj 1845Sumdiv(唯一分解定理&&约数和公式&&二分求等比数列和&&反复平方法计算p^n幂~~~好多定理啊)
- poj_1845 Sumdiv(素因子分解+快速幂+约数和+二分求等比数列和)
- POJ 1845 Sumdiv (唯一分解定理+求等比数列前n项和)
- POJ 1845 Sumdiv(逆元)
- POJ 1845 Sumdiv 求幂级数的因子和+二分
- POJ-1845 Sumdiv 逆元,特殊情况
- POJ-1845-Sumdiv 等比数列求和/数学/(二分法/逆元法/变换取模法)
- POJ 1845 Sumdiv(因子分解+快速幂+二分求和)
- POJ 1845 Sumdiv 逆元 费马小定理 Trick
- POJ 1845 Sumdiv(逆元、分治)【真心好题啊=_=】
- POJ 1845 Sumdiv(质因数分解+快速幂+二分法求等比数列的和)