poj 1845 Sumdiv 美妙的数学

原题连接 : http://poj.org/problem?id=1845
题目大意:求A^B的所有约数之和%9901
刚开始学习数学,觉得这个题实在是太美妙了,有素数筛,有快速幂,有等比数列1+pi+pi^2+pi^3+...+pi^n的快速求和

具体的分析过程是这样的
首先,A可以写成是p1^a1*p2^a2*...*pn^an,的形式,其中p1,p2,...pn是素数
这样的A^B就可以写成是p1^(a1*B)*p2^(a2*B)*...*pn^(an*B)的形式
其中[1+p1+p1^2+...+p1^(a1*B)]*[1+p2+p2^2+...+p2^(a2*B)]*[1+pn+pn^2+...+pn^(an*B)],这个式子展开就是A^B的所有约数,这个的理解,类似于组合数学里面的一个式子,我想不起来了,
这样的话,我们就可以分别求每一个的值,
其中等比数列1+pi+pi^2+pi^3+...+pi^n的值得求法,思路是二分,

思想是把式子从中间断开。

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

若n为偶数,一共有奇数项,设p为4,则(1+p)+p^2+(p^3+p^4)=(1+p)+p^2+p^3(1+p)。

而p^n也可以二分求出,这儿就不介绍了。


当然上面的思路还有一个小小的瑕疵,就是题目中A,B的最大值Max = 50000000,如果对这个数进行素数筛的话,会超时的

所以,上面分解A的方法中的pi怎么处理??

很容易理解,不可能有两个素数,这两个素数都是A的约数,且这两个素数都大于Max^0.5。然后呢??

我们可以筛素数到8000,然后剩下的就是另一个素数了

加上上面思想的话,算法就完美了,

妙不可言啊!!

代码如下:

#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
#define Max 8000
int A,B;

int m = 9901;

int prime[1000];
bool flag[Max];
int getPrime(int max) //筛法求素数
{
    int size = 0;
    memset(flag,0,sizeof(flag));
    for(int i = 2;i < max;i++)
    {
        if(!flag[i])
        {
            for(int j = i + i;j < max;j += i)
                flag[j] = 1;
            prime[size++] = i;
        }
    }
    return size;
}

long long fun(int p,int t)//logn求幂
{
    if (t==1)
		return p;
    long long k=fun(p,t/2);
    if (t&1)
		return k*k%m*p%m;
    else
		return k*k%m;
}

long long deal(int prime,int p)
{
    if(p == 0) return 1;
    long long t = deal(prime,(p-1) >> 1);
    if(p & 1)
        return (t*(fun(prime,(p+1) >> 1) + 1))%m;
    else
        return (fun(prime,p >> 1) + t * (fun(prime,(p>>1)+1)+1)%m)%m;
}
int main()
{
    getPrime(Max);
    while(scanf("%d%d",&A,&B) != EOF)
    {
        if(A == 0) {cout << 0;continue;}
        if(B == 0 || A == 1) {cout << 1;continue;}

        int now = 0;
        long long ans = 1;
        while(A > 1)
        {
            int t = 0;
            while(A%prime[now] == 0)
            {
                t++;A /= prime[now];
            }
            //cout << t << " " << prime[now] << "\n";
            if(t > 0) ans = ans * deal(prime[now],t*B) % m;

            if(A == 1) break;
            if(A/prime[now]<prime[now])//求最大的那个可能大于8000的素数
			{
				ans=ans*deal(A,B)%m;
				break;
			}
			now++;
        }
        cout << ans << "\n";
    }
    return 0;
}


你可能感兴趣的:(poj 1845 Sumdiv 美妙的数学)