【题目大意】

给定\(A\)和\(B\),求\(A^B\)的所有约数之和,对\(9901\)取模。

(对于全部数据,\(0<= A <= B <=50,000,000\))

【算法关键词】

  • 数论
  • 综合模板
  • 二分,乘法逆元

【分析】

不管什么题首先思考的肯定是暴力解法。起码可以骗分啊,当然,如果能一眼标算,那再好不过了。

这道题暴力做法就不说了,其实仔细思考也不会真的打暴力吧。。。

看见约数,首先想到的应该就是数论的有关知识。那么这道题实际上就是在求\(A^B\)的约数之和,问题的难点就在于求解答案的时间优化。

首先,可以思考唯一分解定理,即任意一个自然数都可分解且只能分解成以下形式:

\[n=p_1^{k_1}*p_2^{k_2}*p_3^{k_3}*...*p_m^{k_m}
\]

其中,\(p\)为质因数,\(k\)为自然数。

那如何实现呢?

首先通过线性筛把题目范围内的质数筛出来存在一个数组里,然后枚举数组里的质数是否能被\(A\)整除,即枚举\(p\),如果能,就以\(p\)为底枚举\(k\),并存入相对应的数组。

于是就可分解为:

\[A^B=p_1^{k_1*B}*p_2^{k_2*B}*p_3^{k_3*B}*...*p_m^{k_m*B}
\]

设\(a\)所有约数和为\(ans(a)\)

对于两个互质的数\(a\),\(b\),必定有\(ans(ab)=ans(a)*ans(b)\):

令\(a\)约数为\(x_1\),\(x_2\)...\(x_m\),\(b\)约数为\(y_1\),\(y_2\),...\(y_n\)

由\(a\),\(b\)互质,\(a_1->a_m\)与\(b_2->b_n\)无公共元素

那么

\[ans(ab)=\sum_{i=1,j=1}^{i<=m,j<=n}x_i*y_j\\
\quad=\sum_{i=1}^{m}x_i*\sum_{j=1}^{n}y_j\\
=ans(a)*ans(b)
\]

得证 这不是积性函数的性质吗

同理,\(p_i^{k_i*B}\)与\(p_j^{k_j*B}\)互质

所以:

\[ans(A^B)=\prod_{i=1}^msum_i
\]

其中:

\[sum_i=\sum_{j=0}^{k_i*B}p_i^j
\]

即:

\[ans(A^B)=\prod_{i=1}^{m}\sum_{j=0}^{k_i*B}p_i^j
\]

(理解不透彻的可以把这个拆开看看)

公式推到这里,其实就可以写代码了,在这里,还要注意\(sum_i\)的计算。

\[sum_i=p_i^0+p_i^1+p_i^2+...+p_i^{k_i}
\]

这里有两种方法。

  1. 二分+递归思想

    若\(k_i\)为奇数:

\[sum(p, k) = sum(p, k/2)*(1+p^{n/2+1})
\]

若\(k_i\)为偶数:

\[sum(p, k) = sum(p, k/2-1)*(1+p^{n/2+1})+p^{n/2}
\]

证明的话可以把上面的式子拆开来看,这里不再赘述。

  1. 等比数列通项公式

\[sum(p, k) = \frac{p^{k+1}-1}{p-1}
\]

当然,如果在模的意义下这样做就需要乘法逆元的相关知识,这里就只给出法一的代码。法二的代码可以自己尝试下。其实是因为我懒

#include<cmath>
#include<ctime>
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm> #define debug() puts("FBI WARNING!")
#define ll long long
#define R register using namespace std; /*常量申明*/
const int MAX = 500000;
const int BIG_SEVEN = 777777;
const int MEIZI = 100000;
const int mod = 9901;
/*函数申明*/
void ola(); void test(); void divide(); void cls();void solve();
ll binary_pow(ll, ll); ll sum(ll, ll); ll read();
/*变量申明*/
ll a, b, s, prim[BIG_SEVEN], p[MEIZI], k[MEIZI];
bool vis[MAX + 5];
int cnt, tot; int main(){
ola();
while (~scanf("%lld %lld", &a, &b)) {
cls();
divide();
solve();
//test();
}
return 0;
} inline ll read(){ // 快读 (貌似没用上)
ll f = 1, x = 0;char ch;
do { ch = getchar(); if (ch == '-') f = -1; } while (ch < '0'||ch>'9');
do {x = x*10+ch-'0'; ch = getchar(); } while (ch >= '0' && ch <= '9');
return f*x;
} inline void ola() { // 欧拉筛
vis[1] = 1;
for (R int i = 2;i < MAX; ++i) {
if (!vis[i]) {
prim[cnt++] = i;
}
for (R int j = 0;j < cnt ; ++j) {
if (i*prim[j] > MAX) break;
vis[i*prim[j]] = 1;
if (i%prim[j] == 0) break;
}
}
} inline void cls() {tot = 0, cnt = 0;} // 多组数据初始化 inline void divide() { // 唯一分解定理
memset(k, 0, sizeof(k));ll rose = a;
for (R int i = 0; prim[i] <= rose/prim[i]; ++i) {
if (rose%prim[i] == 0) {
p[tot] = prim[i];
while (rose%prim[i] == 0) {
// debug();
k[tot]++;
rose /= prim[i];
}
tot++;
}
}
if (rose != 1) {
p[tot] = rose;
k[tot++] = 1;
}
} inline void solve() { // 累加答案
ll ans = 1;
for (R int i = 0; i < tot; ++i) {
ans *= (sum(p[i], b*k[i]) %mod);
ans %= mod;
}
printf("%lld\n", ans);
} inline void test() { // 测试函数
/*check ola()*/
for (R int i = 0;i < cnt; ++i) {
cout << prim[i] << " ";
}
printf("%.2lf\n",(double)clock()/CLOCKS_PER_SEC); /*check divide()*/
for (R int i = 0;i < tot; ++i) {
printf("%lld^%lld\n", p[i], k[i]);
}
cout << tot;
} inline ll binary_pow(ll a, ll b) { // 二进制实现的快速模幂
ll ans = 1, tmp = a%mod;
while (b) {
if (b&1) {ans *= tmp; ans %= mod;}
b >>= 1;
tmp *= tmp; tmp %= mod;
}
return ans;
} inline ll sum(ll p, ll k) { // 约数和二分实现(p^0+p^1+...+p^k)
if (p == 0) return 0;
if (k == 0) return 1;
if (k & 1) return ((1+binary_pow(p, k/2+1)) %mod * sum(p, k/2) %mod) %mod; // 奇数
else return ((1+binary_pow(p, k/2+1)) %mod*sum(p, k/2-1) + binary_pow(p, k/2) %mod) %mod;
}

【简●解】POJ 1845 【Sumdiv】的更多相关文章

  1. poj 1845 POJ 1845 Sumdiv 数学模板

    筛选法+求一个整数的分解+快速模幂运算+递归求计算1+p+p^2+````+p^nPOJ 1845 Sumdiv求A^B的所有约数之和%9901 */#include<stdio.h>#i ...

  2. poj 1845 Sumdiv 约数和定理

    Sumdiv 题目连接: http://poj.org/problem?id=1845 Description Consider two natural numbers A and B. Let S ...

  3. POJ 1845 Sumdiv 【二分 || 逆元】

    任意门:http://poj.org/problem?id=1845. Sumdiv Time Limit: 1000MS Memory Limit: 30000K Total Submissions ...

  4. POJ 1845 Sumdiv#质因数分解+二分

    题目链接:http://poj.org/problem?id=1845 关于质因数分解,模板见:http://www.cnblogs.com/atmacmer/p/5285810.html 二分法思想 ...

  5. poj 1845 Sumdiv (等比求和+逆元)

    题目链接:http://poj.org/problem?id=1845 题目大意:给出两个自然数a,b,求a^b的所有自然数因子的和模上9901 (0 <= a,b <= 50000000 ...

  6. POJ 1845 Sumdiv [素数分解 快速幂取模 二分求和等比数列]

    传送门:http://poj.org/problem?id=1845 大致题意: 求A^B的所有约数(即因子)之和,并对其取模 9901再输出. 解题基础: 1) 整数的唯一分解定理: 任意正整数都有 ...

  7. POJ 1845 Sumdiv

    快速幂+等比数列求和.... Sumdiv Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 12599 Accepted: 305 ...

  8. POJ 1845 Sumdiv(逆元)

    题目链接:Sumdiv 题意:给定两个自然数A,B,定义S为A^B所有的自然因子的和,求出S mod 9901的值. 题解:了解下以下知识点   1.整数的唯一分解定理 任意正整数都有且只有唯一的方式 ...

  9. POJ 1845 Sumdiv(求因数和 + 逆元)题解

    题意:给你a,b,要求给出a^b的因子和取模9901的结果. 思路:求因子和的方法:任意A = p1^a1 * p2^a2 ....pn^an,则因子和为sum =(1 + p1 + p1^2 + . ...

  10. POJ 1845 Sumdiv (整数唯一分解定理)

    题目链接 Sumdiv Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 25841   Accepted: 6382 Desc ...

随机推荐

  1. 清除当前文件夹下.svn文件的方法

    把如下命令拷贝到一个文件中,并保存为bat后缀的文件:然后把这个bat文件保存到相应文件夹下. @echo on color 2f mode con: cols=80 lines=25 @REM @e ...

  2. "cannot be resolved or is not a field"问题解决 (转载)

    转自:http://blog.csdn.net/liranke/article/details/16803295 在修改了资源文件后,出现“"cannot be resolved or is ...

  3. git基本操作-常用命令

    git 忽略本地文件 告诉git忽略对已经纳入版本管理的文件 .classpath 的修改,git 会一直忽略此文件直到重新告诉 git 可以再次跟踪此文件$ git update-index --a ...

  4. 【react native】有关入坑3个月RN的心路历程

    由于一些原因,笔者最近变更到了RN的团队,回归到了hybrid app的开发的圈子中,固然是有蛮多新鲜感和新机遇的,不过遥想起以前在hybrid中各种view之前跳转的头疼等各种问题,笔者怀着忐忑的心 ...

  5. django urls路由匹配分发

    django urls路由匹配分发 一.urls配置(URLconf)就像Django 所支撑网站的目录.它的本质是URL与要为该URL调用的视图函数之间的映射表:你就是以这种方式告诉Django,对 ...

  6. Kay and Snowflake CodeForces - 686D

    Kay and Snowflake CodeForces - 686D 题意:给一棵有根树,有很多查询(100000级别的),查询是求以任意一点为根的子树的任意重心. 方法很多,但是我一个都不会 重心 ...

  7. JDBC中Oracle的SID和ServiceName两种方式的连接字符串格式

    SID格式: jdbc:oracle:thin:@<host>:<port>:<SID> 如: jdbc:oracle:thin:@192.168.1.1:1521 ...

  8. 链表中用标兵结点简化代码 分类: c/c++ 2014-09-29 23:10 475人阅读 评论(0) 收藏

    标兵结点(头结点)是在链表中的第一个结点,不存放数据,仅仅是个标记 利用标兵结点可以简化代码.下面实现双向链表中的按值删除元素的函数,分别实现 带标兵结点和不带标兵结点两版本,对比可见标兵结点的好处. ...

  9. selenium 延迟等待的三种方式

    1.最直接普通的方式:这个是设置固定的等待时间    Thread.sleep(1000);   2.显示等待方式(Explicit Wait):就是明确的要等待的元素在规定的时间之内都没找到,那么就 ...

  10. mysql之通过cmd连接远程数据库

    ---恢复内容开始--- 目录 前提 连接远程数据库 前提: 本地安装了mysql数据库 本地和远程网络是连通的,通过命令ping ip (即ping 192.168.0.333),可以ping通 连 ...