【积累】关于因数个数的题



\(d(x)\) 表示 \(x\) 的约数个数 , \(\sigma(x)\) 表示x的约数和;
若正整数 \(m\) 的标准分解式为 \(m=p_1^{a_1}p_2^{a_2} ... p_k^{a_k}\)
\(m\) 所含因数的个数 \(d(m)=(a_1+1)(a_2+1)...(a_k+1)\)
\(m\) 所有正因数的和 \(\sigma(m)=\frac{p_1^{a_1}-1}{p_1-1}*\frac{p_2^{a_2}-1}{p_2-1}*...*\frac{p_k^{a_k}-1}{p_k-1}\)

poi1,洛谷P1403

\(\sum_{i=1}^{n} {d(i)}\)\(n\leq 10^9\);
\([1,n]\) 里有约数 \(i\) 的个数是 \(\lfloor{\frac ni}\rfloor\) ;
类似 整除分块的做法,复杂度\(O(2\sqrt n)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
    int n;
    ll ans=0;
    scanf("%d",&n);
    for(ll l=1,r;l<n;l=r+1)
    {
        r=n/(n/l);
        ans+=(r-l+1)*(n/l);
    }
    printf("%lld\n",ans);
}


poi2,洛谷P2323

\(\sum_{i=l}^{r} {d(i)}\)\(n\leq 10^9\);
做法同poi1:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=5e5+50;
const int inf=0x3f3f3f3f;
int main()
{
    ll x,y,num1=0,num2=0;
    scanf("%lld%lld",&x,&y);
    x--;
    for(ll i=1,j;i<=x;i=j+1)
    {
        j=x/(x/i);
        num1+=(x/i)*(i+j)*(j-i+1)/2;
    }
    for(ll i=1,j;i<=y;i=j+1)
    {
        j=y/(y/i);
        num2+=(y/i)*(i+j)*(j-i+1)/2;
    }
    printf("%lld\n",num2-num1);
}


poi3,洛谷P6060

\(\sum_{i=1}^{n} {d(i^k)}\)\(1\leq{n,k}\leq{10^7}\) ,同时 \(T\) 个测试样例, \(T\leq10^4\);
因为\(10^7\) 内的数最多有8个不同的因子,所以每个数可以预处理成8项式,再预处理前缀和。

假如要筛出大量数能分解的因数个数(比如这道题),那么线性筛的预处理\(O(n+8)\)
不知道该怎么说,下面代码的预处理(参考题解代码来的)很巧妙,(只可意会不可言传

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int mod=998244353;
const int maxn=1e7+5;
const int N=1e7;

int pri[maxn],pp[maxn][9],temp[maxn],pre[maxn];
inline void init()
{
    pp[1][0]=1;
    for(int i=2;i<=N;i++)
    {
        if(!pri[i])
        {
            pri[++pri[0]]=i;pp[i][0]=pp[i][1]=1;pre[i]=1;temp[i]=1;
        }
        for(int j=1,t;j<=pri[0];j++)
        {
            t=i*pri[j];if(t>N)break;
            pri[t]=1;
            if(i%pri[j])temp[t]=1,pre[t]=i;else temp[t]=temp[i]+1,pre[t]=pre[i];
            for(int k=8;k>=1;k--)pp[t][k]=(pp[pre[t]][k]+pp[pre[t]][k-1]*temp[t])%mod;
            pp[t][0]=pp[pre[t]][0];
            if(i%pri[j]==0)break;
        }
    }
    for(int i=2;i<=N;i++)
        for(int j=0;j<=8;j++)pp[i][j]=(pp[i-1][j]+pp[i][j])%mod;
}
int main()
{
    init();
    int T,n,k;
    scanf("%d",&T);
    while(T--)
    {
        ll ans=0;
        scanf("%d%d",&n,&k);
        for(int i=8;i>=0;i--)
            ans=(ans*k%mod+pp[n][i])%mod;
        printf("%lld\n",ans);
    }
}

猜你喜欢

转载自www.cnblogs.com/kkkek/p/12297663.html