zoukankan      html  css  js  c++  java
  • Sumdiv(快速幂+约数和)

    Sumdiv
    Time Limit: 1000MS Memory Limit: 30000K
    Total Submissions: 16244 Accepted: 4044

    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
    思路:優YoU http://user.qzone.qq.com/289065406/blog/1309237394
    大致题意:

    求A^B的所有约数(即因子)之和,并对其取模 9901再输出。

    解题思路:

    要求有较强 数学思维 的题

    应用定理主要有三个:

    要求有较强 数学思维 的题

    应用定理主要有三个:

    (1) 整数的唯一分解定理:

      任意正整数都有且只有一种方式写出其素因子的乘积表达式。
    
      A=(p1^k1)*(p2^k2)*(p3^k3)*....*(pn^kn)   其中pi均为素数
    

    (2) 约数和公式:

    对于已经分解的整数A=(p1^k1)(p2^k2)(p3^k3)….(pn^kn)

    有A的所有因子之和为

    S = (1+p1+p1^2+p1^3+...p1^k1) * (1+p2+p2^2+p2^3+….p2^k2) * (1+p3+ p3^3+…+ p3^k3) * .... * (1+pn+pn^2+pn^3+...pn^kn)
    

    (3) 同余模公式:

    (a+b)%m=(a%m+b%m)%m

    (a*b)%m=(a%m*b%m)%m

    有了上面的数学基础,那么本题解法就很简单了:

    1: 对A进行素因子分解

    分解A的方法:

    A首先对第一个素数2不断取模,A%2==0时 ,记录2出现的次数+1,A/=2;

    当A%2!=0时,则A对下一个连续素数3不断取模…

    以此类推,直到A==1为止。

    注意特殊判定,当A本身就是素数时,无法分解,它自己就是其本身的素数分解式。

    最后得到A = p1^k1 * p2^k2 * p3^k3 pn^kn.
    故 A^B = p1^(k1*B) * p2^(k2*B) pn^(kn*B);

    2:A^B的所有约数之和为:

     sum = [1+p1+p1^2+...+p1^(a1*B)] * [1+p2+p2^2+...+p2^(a2*B)] *...* [1+pn+pn^2+...+pn^(an*B)].
    

    3: 用递归二分求等比数列1+pi+pi^2+pi^3+…+pi^n:

    (1)若n为奇数,一共有偶数项,则:
    1 + p + p^2 + p^3 +…+ p^n

      = (1+p^(n/2+1)) + p * (1+p^(n/2+1)) +...+ p^(n/2) * (1+p^(n/2+1))
      = (1 + p + p^2 +...+ p^(n/2)) * (1 + p^(n/2+1))
    

    上式红色加粗的前半部分恰好就是原式的一半,那么只需要不断递归二分求和就可以了,后半部分为幂次式,将在下面第4点讲述计算方法。

    (2)若n为偶数,一共有奇数项,则:
    1 + p + p^2 + p^3 +…+ p^n

      = (1+p^(n/2+1)) + p * (1+p^(n/2+1)) +...+ p^(n/2-1) * (1+p^(n/2+1)) + p^(n/2)
      = (1 + p + p^2 +...+ p^(n/2-1)) * (1+p^(n/2+1)) + p^(n/2);
    

    上式红色加粗的前半部分恰好就是原式的一半,依然递归求解

    4:反复平方法计算幂次式p^n

    这是本题关键所在,求n次幂方法的好坏,决定了本题是否TLE。

    以p=2,n=8为例

    常规是通过连乘法求幂,即2^8=2*2*2*2*2*2*2*2

    这样做的要做8次乘法

    而反复平方法则不同,

    定义幂sq=1,再检查n是否大于0,

    While,循环过程若发现n为奇数,则把此时的p值乘到sq

    {

    n=8>0 ,把p自乘一次, p=p*p=4 ,n取半 n=4

    n=4>0 ,再把p自乘一次, p=p*p=16 ,n取半 n=2

    n=2>0 ,再把p自乘一次, p=p*p=256 ,n取半 n=1,sq=sq*p

    n=1>0 ,再把p自乘一次, p=p*p=256^2 ,n取半 n=0,弹出循环

    }

    则sq=256就是所求,显然反复平方法只做了3次乘法

    #include <set>
    #include <map>
    #include <list>
    #include <stack>
    #include <cmath>
    #include <queue>
    #include <string>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define PI cos(-1.0)
    #define RR freopen("input.txt","r",stdin)
    
    using namespace std;
    
    typedef long long LL;
    
    const int INF = 0x3f3f3f3f;
    
    const int MOD = 9901;
    
    const int MAX = 1e6;
    
    int prime[100000];
    
    bool vis[MAX];
    
    int top;
    
    int a,b;
    
    int num[20];
    
    int bite[20];
    
    LL Pow(LL p,LL n)//快速幂
    {
        LL ans=1;
        while(n)
        {
            if(n%2)
            {
                ans=(ans*p)%MOD;
            }
            n/=2;
            p=(p*p)%MOD;
        }
        return ans;
    }
    
    LL sum(LL p,LL n)//计算约数和
    {
        if(n==0)
        {
            return 1;
        }
        if(n%2)
        {
            return (sum(p,n/2)*(1+Pow(p,n/2+1)))%MOD;
        }
        else
        {
            return ((sum(p,n/2-1)*(1+Pow(p,n/2+1)))+Pow(p,n/2))%MOD;
        }
    }
    
    void Getprime()//素数筛
    {
        top=0;
        memset(vis,false,sizeof(vis));
        for(LL i=2; i<MAX; i++)
        {
            if(!vis[i])
            {
                prime[top++]=i;
                for(LL j=i*i; j<MAX; j+=i)
                {
                    vis[j]=true;
                }
            }
        }
    }
    
    int main()
    {
        Getprime();
        int ans;
        while(~scanf("%d %d",&a,&b))
        {
            ans=0;
            memset(num,0,sizeof(num));
            for(int i=0; prime[i]*prime[i]<=a; i++)
            {
                if(a%prime[i]==0)//分解因子
                {
                    bite[ans]=prime[i];
                    while(a%prime[i]==0)
                    {
                        a/=prime[i];
                        num[ans]++;
                    }
                    ans++;
                }
            }
            if(a!=1)
            {
                bite[ans]=a;
                num[ans]++;
                ans++;
            }
            int ant=1;
            for(int i=0;i<ans;i++)
            {
                ant=(ant*(sum(bite[i],num[i]*b)))%MOD;
            }
            printf("%d
    ",ant);
        }
        return 0;
    }
    
  • 相关阅读:
    FZU 2098 刻苦的小芳(卡特兰数,动态规划)
    卡特兰数总结
    FZU 1064 教授的测试(卡特兰数,递归)
    HDU 4745 Two Rabbits(区间DP,最长非连续回文子串)
    Java 第十一届 蓝桥杯 省模拟赛 正整数的摆动序列
    Java 第十一届 蓝桥杯 省模拟赛 反倍数
    Java 第十一届 蓝桥杯 省模拟赛 反倍数
    Java 第十一届 蓝桥杯 省模拟赛 反倍数
    Java 第十一届 蓝桥杯 省模拟赛 凯撒密码加密
    Java 第十一届 蓝桥杯 省模拟赛 凯撒密码加密
  • 原文地址:https://www.cnblogs.com/juechen/p/5255952.html
Copyright © 2011-2022 走看看