zoukankan      html  css  js  c++  java
  • 【数论】POJ1845 Sumdiv

    最近本渣渣做了一道快搞死我的题,就是这个!

    下面隆重给出题目以及链接

    Sumdiv
    Time Limit: 1000MS   Memory Limit: 30000K
    Total Submissions: 29696   Accepted: 7312

    Description

    Consider two natural numbers A and B. Let S be the sum of all natural divisors of A^B. Determine S modulo 9901 (the rest of the division of S by 9901).

    Input

    The only line contains the two natural numbers A and B, (0 <= A,B <= 50000000)separated by blanks.

    Output

    The only line of the output will contain S modulo 9901.

    Sample Input

    2 3

    Sample Output

    15

    Hint

    2^3 = 8. 
    The natural divisors of 8 are: 1,2,4,8. Their sum is 15. 
    15 modulo 9901 is 15 (that should be output). 

    题目一眼就能看懂对吧??不就是求AB所有因子的和嘛!我第一眼看到题目这么短就心知不好,绝对有坑啊!!搜了一下,竟然要用到数论的知识,学离散的时候数论就学的不好,为了做这道题,把离散初等数论的章节又从头到尾复习了一遍。下面罗列一下这道题要用到的知识点:

    埃氏筛法

    这是里面最简单的一个知识点了!关于埃氏筛法前几天刚好写了篇随笔,可以戳一下看看。在这里是用来筛素的,提供一个素数表,方便后面的计算。

    这是模板:

     1 //埃氏筛法筛素
     2 const int MAX_N = 10005;
     3 int prime[MAX_N];
     4 bool is_prime[MAX_N];
     5 void getPrime(){
     6     int p = 0;
     7     for(int i = 2; i < MAX_N; i++) is_prime[i] = true;
     8     for(int i = 2; i < MAX_N; i++){
     9         if(is_prime[i]){
    10             prime[p++] = i;
    11             for(int j = 2*i; j < MAX_N ; j+=i) is_prime[j] = false;
    12         }
    13     }
    14 }

    算术基本定理

    这也是数论里的一个知识点:任何大于1的整数要么是素数,要么可以分解成素数的乘积,这样的分解是唯一的。

    也就是说,题目中的A,我们可以把它分解成这样的形式:A = p1k* p2k... * pnkn   (pi是素数且ki>0)

    比如36,我们可以把它分解22 * 32,其中的23都是素数;再比如44,我们可以把它分解成22 * 111,其中的211都是素数。

    把这个弄懂之后,显然可以得到:AB = p1k1B * p2k2B ... * pnknB   (pi是素数且ki>0)

    下面是代码,我们要得到 p以及 k的数组,以及A的素因子的个数,方便后面的计算:

     1 //素因子分解 
     2 int p[MAX_N],k[MAX_N];
     3 int cnt;
     4 void getFactors(long long x){
     5     memset(k,0,sizeof(k));
     6     cnt = 0;
     7     for(int i = 0; prime[i]*prime[i] <=x ; i++){
     8         if(x % prime[i] == 0){ 
     9             p[cnt] = prime[i];
    10             while(x % prime[i] == 0){
    11                 k[cnt]++;
    12                 x /= prime[i];
    13             }
    14             cnt++;
    15         }
    16     }
    17     if(x != 1){   
    18         p[cnt] = x;
    19         k[cnt++] = 1;
    20     }
    21 }

    可能有人(本渣渣)会觉得奇怪,最后那步x!=1是怎么回事呢?注意,在前面for循环的部分,循环条件写的是prime[i]*prime[i]<=x,也就是说,我们只考虑了小于等于√x的素因子。而x最多只可能有一个大于x的素因子(由算术基本定理,x可以分解为素数的乘积,若有两个大于x的素因子,那么乘积将大于x),以x除以x范围内的所有素因子后如果仍不为1,那么此时剩下的x一定是一个素数,即最后一个素因子。

    约数和定理

    对于已经分解的整数 A = p1k* p2k... * pnkn   (pi是素数且ki>0)

    有A的所有因子之和为 S = (p10+p11+p12+...p1k1) * (p20+p21+p22+...p2k2) ... * (pn0+pn1+pn2+...pnkn)

    很容易可以想到,每一项 piki 都有 ki+1 个因子,即 pi0 、pi、pi2  ... pik,所以 pik的因子和就是 p10+p11+p12+...p1k1 。(不难看出,这是一个等比数列。)现在要求A的因子和,因为A是 pik的乘积,所以只要把每一项 piki 的因子和都相乘就可以了。(这是我的理解,想看更严密的证明可以百度一下)

    快速幂运算

    快速幂也是很基础的一个点了,刚好前段时间也写了随笔,可以戳这里

    代码在这里:

     1 //快速幂运算
     2 long long quickPow(long long a,long long n){
     3     long long res = 1;
     4     while(n){
     5         if(n & 1)
     6             res = res * a % MOD;
     7         a =  a * a % MOD;
     8         n >>= 1;
     9     }
    10     return res;
    11 }

    等比数列二分求和

    这个大概是这道题的核心了!从上面给出的约数和定理,我们发现A的所有因子之和是等比数列和的乘积。因为一般的等比数列求和公式中有除法,所以求和时不能直接用公式。据说本题有三种做法,二分,逆元,变换取模。本渣渣现在只会第一种,所以先介绍一下二分求和的方法~

    观察等比数列的式子 a + a2 + ... + an ,我们发现:

    (1)时,

    (2) n 为偶数时,

    (3) n 为奇数时,

    这边我们用到的式子还要再加上1,公式同样也可以像上面这样推出来:

    n为奇数,Sn = (1 + an/2+1) * Sn/2 

    n为偶数,Sn = (1 + an/2+1)Sn/2-1 + an/2 

     下面给出代码:

     1 //等比数列二分求和
     2 long long sum(long long p,long long n){  //计算1+p+p^2+...+p^n
     3     if(p == 0) return 0;
     4     if(n == 0) return 1;
     5     if(n & 1)
     6         return ((1 + quickPow(p,n/2+1)) * sum(p,n/2)) % MOD;
     7     else
     8         return ((1+quickPow(p,n/2+1)) * sum(p,n/2-1) + quickPow(p,n/2)) % MOD;
     9 
    10 }

    最后!

    下面就可以开始写main函数啦,注意注意,一定要小心取模,本渣渣在这边wa了n次,最后才找到问题。话不多说,上代码:

     1 int main(){
     2     long long A,B;
     3     getPrime();   //得到素数表
     4     while(cin>>A>>B){
     5         getFactors(A);  //分解质因子
     6         long long ans = 1;
     7         for(int i = 0; i < cnt; i++){
     8             ans = ans * sum(p[i],k[i]*B) % MOD; 
     9         }
    10         cout<<ans<<endl;
    11     }
    12 
    13     return 0;
    14 }

    筒子萌!!有没有看到那个for循环!!有没有看到里面的 ans = ans * sum(p[i],k[i]*B) % MOD; 这句!

    我之前写的是:ans *= sum(p[i],k[i]*B) % MOD; 然后在for循环外面又写了个 ans %= MOD; 自以为万无一失了,然后。。我真的查了很久的错QAQ。。疯狂wa,内心是崩溃的!

    注意在取模运算啥的时候能不写这种 *=+= 咱就别写啊,真的挺坑的(大佬请自动忽视我这句话),我前面随处都在留意着取模,没想到这边疏忽了,功亏一篑啊。

    下面是完整的代码,有兴趣的可以参考一下,另附我参考的几个题解(题解一    题解二)和POJ讨论区的一些测试数据

     1 #include <iostream>
     2 #include <cstring>
     3 using namespace std;
     4 
     5 #define MOD 9901
     6 
     7 // 求A^B的所有约数之和%9901
     8 
     9 //埃氏筛法筛素
    10 const int MAX_N = 10005;
    11 int prime[MAX_N];
    12 bool is_prime[MAX_N];
    13 void getPrime(){
    14     int p = 0;
    15     for(int i = 2; i < MAX_N; i++) is_prime[i] = true;
    16     for(int i = 2; i < MAX_N; i++){
    17         if(is_prime[i]){
    18             prime[p++] = i;
    19             for(int j = 2*i; j < MAX_N ; j+=i) is_prime[j] = false;
    20         }
    21     }
    22 }
    23 
    24 
    25 //素因子分解 A = p1^k1 * p2^k2 * ... * pn^kn
    26 int p[MAX_N],k[MAX_N];
    27 int cnt;
    28 void getFactors(long long x){
    29     memset(k,0,sizeof(k));
    30     cnt = 0;
    31     for(int i = 0; prime[i]*prime[i] <=x ; i++){
    32         if(x % prime[i] == 0){  //如果x能被当前素数整除
    33             p[cnt] = prime[i];
    34             while(x % prime[i] == 0){
    35                 k[cnt]++;
    36                 x /= prime[i];
    37             }
    38             cnt++;
    39         }
    40     }
    41     if(x != 1){   //如果x不为1,剩下的一定是大于等于√x的一个素因子
    42         p[cnt] = x;
    43         k[cnt++] = 1;
    44     }
    45 }
    46 
    47 //带模快速幂运算
    48 long long quickPow(long long a,long long n){
    49     long long res = 1;
    50     while(n){
    51         if(n & 1)
    52             res = res * a % MOD;
    53         a =  a * a % MOD;
    54         n >>= 1;
    55     }
    56     return res;
    57 }
    58 
    59 //等比数列二分求和
    60 long long sum(long long p,long long n){  //计算1+p+p^2+...+p^n
    61     if(p == 0) return 0;
    62     if(n == 0) return 1;
    63     if(n & 1)
    64         return ((1 + quickPow(p,n/2+1)) * sum(p,n/2)) % MOD;
    65     else
    66         return ((1+quickPow(p,n/2+1)) * sum(p,n/2-1) + quickPow(p,n/2)) % MOD;
    67 
    68 }
    69 
    70 int main(){
    71     long long A,B;
    72     getPrime();   //得到素数表
    73     while(cin>>A>>B){
    74         getFactors(A);  //分解质因子
    75         long long ans = 1;
    76         for(int i = 0; i < cnt; i++){
    77             ans = ans * sum(p[i],k[i]*B) % MOD;
    78         }
    79         cout<<ans<<endl;
    80     }
    81 
    82     return 0;
    83 }
    View Code
  • 相关阅读:
    让你爱不释手的图片浮动效果
    Polymer API开发指南 (二)(翻译)
    基于HTML5的拓扑图编辑器(2)
    kbengine开源分布式游戏服务端引擎
    Qunee for HTML5 v1.6新版本发布
    [转载] Link prefetch
    小白学phoneGap《构建跨平台APP:phoneGap移动应用实战》连载三(通过实例来体验生命周期)
    云集,让 web app 像 native app 那样运行(雄起吧,Web 开发者)
    Android设置ToolBar的title文字居中显示
    Task 'assembleXXXDebug' not found in project ':app'.的解决方法
  • 原文地址:https://www.cnblogs.com/Aikoin/p/10221848.html
Copyright © 2011-2022 走看看