poj 1845(快速幂+二分计算等比数列和+大数因子分解+因子和计算+模除溢出)

Sumdiv
Time Limit: 1000MS   Memory Limit: 30000K
Total Submissions: 12755   Accepted: 3100

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).  

Source

Romania OI 2002

题目非常好,考察的技巧很多。
1、同模余定理:
%运算对于加减乘法是随意的。可以随便打开括号。因此为了避免溢出,要经常做%运算。
2、等比数列求和:
1 + 2 + 2^2 + 2^3 ... + 2^n 计算的时候,不能用等比求和公式,因为 除法不满足同模余定理,在求余数的时候会有错误。因此使用一个小trick,用二分来解决(非常巧妙):
    当n为偶数时: 1+2+...+2^n  =  (1 + 2 + ... + 2 ^ (n / 2)) * (1 + 2 ^ (n / 2 + 1)) - 2 ^ (n + 1)
    当n为奇数时: 1+2+...+2^n  =  (1 + 2 + ... + 2 ^ (n / 2)) * (1 + 2 ^ (n / 2 + 1))
3、大数因子分解:
    任意一个自然数都可以分解为素数次幂的乘积,而且 分解方式唯一。也就是说,分解方式与原自然数 是 一一对应的!
     比如300 可以分解为 2 * 2 * 3 * 5 * 5
     用数组表示就是:
        素数:         2   3   5
        对应个数: 2   1   2
     分解算法千万不要去打素数表,因为当输入非常大(比如这题的5000万),打素数表会直接超时,即使用筛法)
     直接去分解,就会自然形成素数序列(有点类似筛素数了,比如2被分解干净后,2的倍数就不会再被分解出来)
    cin >> a >> b;
    ll tmp = a;
    int num = 0;
    for (i = 2; tmp != 1 && i * i <= a; i++) { //这里是tmp != 1,终止条件是i*i <= a !!!因为所有因子中,最坏情况只能是有一个素因子大于sqrt(a),因此最后特殊考虑一下即可。
        while (tmp % i == 0) {
            p[num] = i;    
            n[num] ++;
            tmp /= i;
        }
        if (p[num] != 0) num++;
    }
    if (tmp != 1) { //这里是tmp != 1!!!
        p[num] = tmp;
        n[num] = 1;
        num++;
    }


4、大数因子和:
     分解出上面的数组后,其实因子和就很容易求出来了:
     其实就是(1 + 2 + 2^2) * (1 + 3) * (1 + 5 + 5^2)    想想为什么? 其实 原数的因子就是 在(1, 2, 4), (1, 3), (1, 5, 25)  这三个集合中各挑一个,然后相乘,就可以形成一个因子。
因此一共会有3 * 2 * 3种挑选方法,亦即3*2*3个因子。
     我刚开始思考的时候,是想用dp的方法计算这些因子中,各个余数的数量,然后求和。这么做显然会超时。 其实就是原来的 括号内先求和 ,再相乘。就等同于把括号打开后计算乘法后再相加。
5、快速幂算法:
    这个我不想多说了,这次居然又写错一次,还另外溢出WA了一次:
    
ll pow(ll a, ll b) {
    ll result = 1;
    while (b > 0) {
        if (b % 2 == 1) {
            result = result * a % mod;
        }
        b /= 2;
        a = a * a % mod; //这里要写对!!而且要加上mod
    }
    return result;
}


提交记录:
1、WA。因为没有考虑最后大于sqrt(n)的那 一个素因子。
2、WA。素因子分解的时候,循环条件应该是tmp != 1,而不是tmp != 0。因此这里只有可以整除的时候才会调用/, 所以不会除到0.
3、WA。每一次计算出来的result,都要去%mod,否则 可能溢出
4、WA。快速幂中德a = a * a 一定要改为a = a * a % mod ,否则 会溢出
5、WA。在计算cal中,减去的时pow(a, b+1),这里粗心了,写成了pow(a, b).
6、WA。为了保险, 最后结果如果为负数的时候,要先加上一个mod,再模除一下。
7、AC

代码:

你可能感兴趣的:(poj 1845(快速幂+二分计算等比数列和+大数因子分解+因子和计算+模除溢出))