先上公式:
因子和:
如果p为素数,a为整数,那么p^a 的因子和为
1 + p + p^2 + p ^3+…+p ^a=(p ^(a+1) - 1)/(p-1);(等比数列求和)
如果对于对于一个整数数 n,可以进行质因数分解成 p1^a1 * p2 ^a2 p3 ^a3…pk ^ak
那么因子和为:
多个等比数列求和式的和。
因子个数:
(a1+1)(a2+1)…(ak+1)

poj2992
求一个组合数的因子的个数,组合数可以用阶乘表示,即转化为如何求一个数的阶乘的质因子的个数。
这题对时间的限制特别紧。
首先,可能输入量比较大,所以你不能输入一个求一个,要先把答案预处理来。
其次不能用递归来预处理每个数的阶乘中所含的素因子的个数,否则还会超时,要用递推。

#include <cstdio>
#include <cstring>
using namespace std;
const int N=440;
int prime[N],cnt;
long long an[440][440];
bool vis[N];
long long cal[440][100];
void eulers()//欧拉筛筛素数
{
    memset(vis,false,sizeof(vis));
    //memset(prime,0,sizeof(prime));
    cnt=0;
    for(int i=2;i<N;i++)
    {
        if(!vis[i])
            prime[++cnt]=i;
        for(int j=1;j<=cnt&&i*prime[j]<N;j++)
        {
            vis[i*prime[j]]=1;
            if(i%prime[j]==0)
                break;
        }
    }
}
void init()
{
    for(int i=1;i<=cnt;i++)
    {
        for(int j=0;j<=431;j++)
            cal[j][i]=j/prime[i]+cal[j/prime[i]][i];//根据上面的公式
    }
    for(int i=0;i<=431;i++)
    {
        for(int j=0;j<=i;j++)
        {
            an[i][j]=1;
            for(int k=1;k<=cnt;k++)
                an[i][j]*=(cal[i][k]-cal[i-j][k]-cal[j][k]+1);
        }
    }
}
int main()
{
    eulers();
    init();
    int n,k;
    while(scanf("%d%d",&n,&k)!=EOF)
        printf("%lld\n",an[n][k]);
    return 0;
}

递归求法:

ll cal(ll num,ll pri)
{
    return num<pri?0:num/pri+cal(num/pri,pri);
}

poj1845
大概思路很明显,利用求因子的公式。
求等比数列和,因为数据范围,所有不能先用欧拉筛来于处理,可以输入a,b后对a进行质因子分解。(错了好多次)求出每个质因子的幂。因为有除的操作,所有要处理,但不能用费马小定理和拓展欧几里得,因为不能保证互质。所有用了一种新的方便的方法处理。注意数据数据类型的转换。(WA了好几次)。
另一种通用求逆元的方法
(a/b)mod m=a mod(m*b)/b

#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
const ll mod=9901;//WA
ll M;
int a,b;
ll mul(ll a,ll b)
{
    ll res=0;
    while(b)
    {
        if(b&1)
            res=(res+a)%M;
        a=(a+a)%M;
        b>>=1;
    }
    return res;
}
ll POW(ll a,ll b)
{
    ll res=1;
    while(b)
    {
        if(b&1)
            res=mul(res,a);
        a=mul(a,a);
        b>>=1;
    }
    return res;
}
int main()
{
    while(scanf("%d%d",&a,&b)!=EOF)
    {
        ll ans=1;
        for(int i=2;i*i<=a;i++)
        {
            if(a%i==0)
            {
                int num=0;
                while(a%i==0)
                {
                    a/=i;
                    num++;
                }
                M=(i-1)*mod;
                ans=ans*(POW(i,num*b+1)+M-1LL)/(i-1)%mod;//防止为负+M
            }
        }
        if(a>1)
        {
            M=(a-1)*mod;
            ans=ans*(POW(a,b+1)+M-1LL)/(a-1)%mod;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

hdu1452
思路和上面一致,注意快速幂里面的乘***爆long long ,要用快速乘。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=29;
ll x,cnt,M;
ll fax[20],e[20]={0};
void init()
{
    ll a=2004;
    cnt=0;
    for(int i=2;i*i<=2004;i++)
    {
        if(a%i==0)
        {
            fax[++cnt]=i;
            while(a%i==0)
            {
                a/=i;
                e[cnt]++;
            }
        }
    }
    if(a>1)
    {
        fax[++cnt]=a;
        e[cnt]++;
    }
}
ll mul(ll a,ll b)
{
    ll res=0;
    while(b)
    {
        if(b&1)
            res=res+a%M;
        a=a+a%M;
        b>>=1;
    }
    return res%M;
}
ll POW(ll a,ll b)
{
    ll res=1;
    while(b)
    {
        if(b&1)
            res=mul(res,a);
        a=mul(a,a);
        b>>=1;
    }
    return res;
}
int main()
{
    init();
    while(scanf("%lld",&x),x)
    {
        ll ans=1;
        for(int i=1;i<=cnt;i++)
        {
            M=(fax[i]-1)*mod;
            ans=ans*(POW(fax[i],e[i]*x+1)+M-1LL)/(fax[i]-1)%mod;
        }
        printf("%lld\n",ans);
    }
    return 0;
}