解题报告:luogu P1593

题目链接:P1593 因子和
是个好题(考试的时候写题解
这个证明和因子个数定理的证明是异曲同工的,想看的话:Link
考虑\(a\)唯一分解式:
\[a=p_1^{k_1}p_2^{k_2}......p_n^{k_n}\]
显然进行\(b\)次幂后是这样的:
\[a=p_1^{bk_1}p_2^{bk_2}......p_n^{bk_n}\]
那么我们得到的所有约数都是从这里来的:
简单手玩一下,发现是个\(sb\)分配率问题,
那么最后的得数应该是:
\[ans=\prod\limits_{i=1}^n\sum\limits_{j=0}^{bk_i}p_i^j\]
发现后面是个等比数列,搞一下就好了:
\[ans=\prod\limits_{i=1}^n\dfrac{p_i^{bk_i+1}-1}{p_i-1}\]

\(Code\):

#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
const int mod=9901;
ll qp(ll a,ll b)
{
    ll ans=1,base=a;
    while(b)
    {
        if(b&1) ans=ans*base%mod;
        b>>=1;
        base=base*base%mod; 
    }
    return ans%mod;
}
ll inv(ll x){if(x==1) return 1;else return qp(x,mod-2)%mod;}
ll a,b,sum=1;
ll sol(ll x,ll y)
{
    int cnt=0;
    for(int i=2;(ll)(i*i)<=x;i++)
    {
        if(x%i==0)
        {
            cnt=1,x/=i;
            while(x%i==0) x/=i,cnt++;
            sum=sum*((qp(i,y*cnt+1)-1+mod)%mod*inv(i-1)%mod)%mod;
        }   
    }
    if(x>=2) sum=sum*((qp(x,y+1)-1+mod)%mod*inv(x-1)%mod)%mod;
    return sum%mod;
}
int main()
{
    scanf("%lld%lld",&a,&b);
    printf("%lld\n",sol(a,b));
    return 0;
}

然后就\(WA\)了三个点。
发现\(a\)可以为\(0\),特判一下,就又对了一个点,这里不放代码了。
一脸懵逼的我只好去翻讨论区,发现了端倪:
如果\((p_i-1)|MOD\),就没有逆元!
怎么办呢:
有两种方法:
1.若\(\dfrac{a}{b}\;mod\;c\)时,\(a\)较大、\(b,c\)较小,且满足\(b|a,c|b\),时,原始的值\(=\dfrac{a\;mod\;bc}{b}\),算就行了,原理也是很好理解的:

\(Code\)

#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
const ll mod=9901;
ll qp(ll a,ll b,ll p)
{
    ll ans=1,base=a;
    while(b)
    {
        if(b&1) ans=ans*base%p;
        b>>=1;
        base=base*base%p;   
    }
    return ans%p;
}
ll inv(ll x){if(x==1) return 1;else return qp(x,mod-2,mod);}
ll a,b,sum=1;
ll sol(ll x,ll y)
{
    int cnt=0;
    for(int i=2;(ll)(i*i)<=x;i++)
    {
        if(x%i==0)
        {
            cnt=1,x/=i;
            while(x%i==0) x/=i,cnt++;
            if((i-1)%mod) sum=sum*((qp(i,y*cnt+1,mod)-1+mod)%mod*inv(i-1)%mod)%mod;
            else sum=sum*((qp(i,y*cnt+1,mod*(i-1))-1+mod*(i-1))%(mod*(i-1))/(i-1))%mod;
        }   
    }
    if(x>=2)
    {
        if((x-1)%mod) sum=sum*((qp(x,y+1,mod)-1+mod)%mod*inv(x-1)%mod)%mod;
        else sum=sum*((qp(x,y+1,mod*(x-1))-1+mod*(x-1))%(mod*(x-1))/(x-1))%mod;
    }
    return sum%mod;
}
int main()
{
    scanf("%lld%lld",&a,&b);
    if(a==0) return puts("0"),0;  
    else printf("%lld\n",sol(a,b));
    return 0;
}

然后就有大佬得出了更妙的方法:
2.考虑若:\((p-1)|mod\),则\(p\;mod\;MOD=1\)(变量名混乱),那么\((p^0+p^1+p^2+......p^bk)\;mod\;MOD=(1+1+1+......+1)=bk+1\),当然最后的余数也要相似地处理一下:

\(Code\)

#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
const ll mod=9901;
ll qp(ll a,ll b,ll p)
{
    ll ans=1,base=a;
    while(b)
    {
        if(b&1) ans=ans*base%p;
        b>>=1;
        base=base*base%p;   
    }
    return ans%p;
}
ll inv(ll x){if(x==1) return 1;else return qp(x,mod-2,mod);}
ll a,b,sum=1;
ll sol(ll x,ll y)
{
    int cnt=0;
    for(int i=2;(ll)(i*i)<=x;i++)
    {
        if(x%i==0)
        {
            cnt=1,x/=i;
            while(x%i==0) x/=i,cnt++;
            if((i-1)%mod) sum=sum*((qp(i,y*cnt+1,mod)-1+mod)%mod*inv(i-1)%mod)%mod;
            else sum=sum*(y*cnt+1)%mod;
        }   
    }
    if(x>=2)
    {
        if((x-1)%mod) sum=sum*((qp(x,y+1,mod)-1+mod)%mod*inv(x-1)%mod)%mod;
        else sum=sum*(y+1)%mod;
    }
    return sum%mod;
}
int main()
{
    scanf("%lld%lld",&a,&b);
    if(a==0) return puts("0"),0;  
    else printf("%lld\n",sol(a,b));
    return 0;
}

关于复杂度

容易发现,不管是那种方法(保证正确的前提下),复杂度都是\(O(\sqrt{a}+\log ab)\),大概是这样,不过是跑不满的,可以通过本题。

猜你喜欢

转载自www.cnblogs.com/tlx-blog/p/12539918.html