[Spoj]Counting Divisors (cube)

来自FallDream的博客,未经允许,请勿转载,谢谢。



设d(x)表示x的约数个数,求$\sum_{i=1}^{n}d(i^{3})$

There are 5 Input files.

- Input #1: 1≤N≤10000, TL = 1s.

- Input #2: 1≤T≤300, 1≤N≤10^8, TL = 20s.

- Input #3: 1≤T≤75, 1≤N≤10^9, TL = 20s.

- Input #4: 1≤T≤15, 1≤N≤10^10, TL = 20s.

- Input #5: 1≤T≤2, 1≤N≤10^11, TL = 20s.

$i^{3}$的约数个数$d(i^{3})$是一个积性函数,所以转而求$d(x)=\prod{F(pi^{ci})}$,其中$F ( pk ^ {ck} )=3ck+1$

可以直接洲阁筛 学了一天大概懂了 顺便抄了个模板 给个链接吧:大佬博客

-----

gi表示1-i中与前j个质数互质的数字的F之和

fi表示1-i中由小于根号n的后j个质数组成的数字的F之和

容易得出转移方程 $$g[i][j]=g[i][j]-F(pk)g[\frac{i}{pk}][j-1]$$

$$ f[i][j]=f[i][j-1]+\sum_{ck>=1}F(pk^{ck})f[\frac{i}{pk^{ck}}][j]$$

显然i只有根号种取值 对于每个根号n以内的质数都要转移,复杂度$O(\frac{n}{\log n})$

考虑优化,显然$p_{j+1}>i$的时候,g[i][j]=4(3*1+1)

所以当$pj^{2}>i$的时候,g[i][j]=g[i][j-1]+F(pi) 可以不用转移,用的时候补上那一段即可。

之所以把f的状态表示成"后j个",也是出于这个目的

这样的复杂度近似是$O(\frac{n^{\frac{3}{4}}}{logn})$

然后线筛出根号n以内的F[],答案是$f[n]+\sum_{i=1}^{\sqrt{n}}F[i]g[\frac{n}{i}]$

#include<iostream>
#include<cstdio>
#include<cmath>
#define MN 320000
#define ll long long
using namespace std;
inline ll read()
{
    ll x = 0; char ch = getchar();
    while(ch < ‘0‘ || ch > ‘9‘) ch = getchar();
    while(ch >= ‘0‘ && ch <= ‘9‘){x = x * 10 + ch - ‘0‘;ch = getchar();}
    return x;
}

int s[MN+5],num=0,last[MN+5],l[MN+5],l0[MN+5],sq,P,N[MN+5];
ll f0[MN+5],f[MN+5],g0[MN+5],g[MN+5],d[MN+5],n;
bool b[MN+5];

void CalcF()
{
    for(int i=1;i<=sq;++i) f[i]=f0[i]=1;
    for(int i=P-1;i;--i)
    {
        for(int j=1;j<=sq&&l[j]>i;++j)
        {
            ll now=(n/j)/s[i];
            for(int tms=4;now;now/=s[i],tms+=3)
            {
                if(now<=sq) f[j]+=tms*(f0[now]+4*(max(0,N[now]-max(i+1,l0[now])+1)));
                else f[j]+=tms*(f[n/now]+4*max(0,P-max(i+1,l[n/now])));
            }
        }
        for(int j=sq;j&&l0[j]>i;--j)
        {
            ll now=j/s[i];
            for(int tms=4;now;tms+=3,now/=s[i])
                f0[j]+=tms*(f0[now]+4*max(0,N[now]-max(i+1,l0[now])+1));
        }
    }
    for(int i=1;i<=sq;++i) f[i]+=4*(P-l[i]);
}

void CalcG()
{
    for(int i=1;i<=sq;++i)
        g0[i]=i,g[i]=n/i;
    for(int i=1;i<P;++i)
    {
        for(int j=1;j<=sq&&l[j]>i;++j)
        {
            ll now=n/j/s[i];
            if(now<=sq) g[j]-=g0[now]-max(0,i-l0[now]);
            else g[j]-=g[n/now]-max(0,i-l[n/now]);
        }
        for(int j=sq;j&&l0[j]>i;--j)
            g0[j]-=g0[j/s[i]]-max(0,i-l0[j/s[i]]);
    }
    for(int i=1;i<=sq;++i) g[i]-=P-l[i];
}

int main()
{
    d[1]=1;
    for(int i=2;i<=MN;++i)
    {
        if(!b[i]) s[++num]=last[i]=i;
        for(int j=1;s[j]*i<=MN;++j)
        {
            b[s[j]*i]=1,last[s[j]*i]=s[j];
            if(i%s[j]==0) break;
        }
        int sum=1,tms,p;
        for(int j=i;j>1;)
        {
            tms=0;p=last[j];
            for(;j%p==0;j/=p,++tms);
            sum*=(tms*3+1);
        }
        d[i]=sum;
        N[i]=N[i-1]+(!b[i]);
    }
    for(int T=read();T;--T)
    {
        n=read();sq=sqrt(n);l[sq+1]=0;
        for(P=1;1LL*s[P]*s[P]<=n;++P);
        for(int i=1;i<=sq;++i)
            for(l0[i]=l0[i-1];1LL*s[l0[i]]*s[l0[i]]<=i;++l0[i]);
        for(int i=sq;i;--i)
            for(l[i]=l[i+1];1LL*s[l[i]]*s[l[i]]<=n/i;++l[i]);
        CalcF();CalcG();
        ll ans=f[1];
        for(int i=1;i<=sq;++i)
            ans+=4*d[i]*(g[i]-1);
        printf("%lld\n",ans);
    }
    return 0;
}
时间: 2024-10-24 16:11:38

[Spoj]Counting Divisors (cube)的相关文章

Counting Divisors HDU - 6069

Counting Divisors HDU - 6069 题意:给定区间[a,b]和k,求xk有多少因子(x属于[a,b]),求和. 题解:http://blog.csdn.net/zlh_hhhh/article/details/76680641 a.b最大可达到1e12,但是b-a<1e6. 一开始愚蠢的一个一个分解然后去求有多少因子然后求和,范围那么大裸裸的超时啊! 可以枚举素数,对每一个素数,把区间内所有可以分解的进行分解. 最后再求和. 1 #include <bits/stdc++

HDU 6069 Counting Divisors —— 2017 Multi-University Training 4

Counting Divisors Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 524288/524288 K (Java/Others)Total Submission(s): 2599    Accepted Submission(s): 959 Problem Description In mathematics, the function d(n) denotes the number of divisors of p

hdu 6069 Counting Divisors(求因子的个数)

Counting Divisors Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 524288/524288 K (Java/Others)Total Submission(s): 3170    Accepted Submission(s): 1184 Problem Description In mathematics, the function d(n) denotes the number of divisors of

SPOJ : DIVCNT2 - Counting Divisors (square)

设 \[f(n)=\sum_{d|n}\mu^2(d)\] 则 \[\begin{eqnarray*}\sigma_0(n^2)&=&\sum_{d|n}f(d)\\ans&=&\sum_{i=1}^n\sigma_0(i^2)\\&=&\sum_{i=1}^n\sum_{d|i}\sum_{k|d}\mu^2(k)\\&=&\sum_{k=1}^n\mu^2(k)G(\lfloor\frac{n}{k}\rfloor)\end{eqnarr

SPOJDIVCNT2: Counting Divisors(莫比乌斯反演)

http://acm.tzc.edu.cn/acmhome/vProblemList.do?method=problemdetail&oj=SPOJ&pid=DIVCNT2 给出n求 其中是除数函数,0代表0次方. 1 #include<algorithm> 2 #include<cstdio> 3 #include<cmath> 4 #include<cstring> 5 #include<iostream> 6 #define

【区间筛】2-17多校训练四 HDU6069 Counting Divisors

http://acm.hdu.edu.cn/showproblem.php?pid=6069 [题意] 给定l,r,k,求 d(n)是n的因子个数 [思路] [Accepted] 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<string> 5 #include<cmath> 6 #include<algorithm> 7 #include

第四场 hdu 6069 Counting Divisors (逆向思维)

http://acm.hdu.edu.cn/showproblem.php?pid=6069 题目大意:求 i 从 l 到 r 中 i 的k次方的因子数之和. 解题思路:我们可以知道一个数有因子,则这个数的因子一定是若干个质数因子排列组合得到的.我们首先要得到10^6中的素数,然后它的因子数量是 相同质因子数量+1 的乘积,所以我们能够想到从 l 到 r 枚举每一个i得到其 相同质因子数量+1 的乘积 的累加和.但是这样在枚举时会发现有一些质数是并不是所求的 i 的因子,所以我们应该反过来考虑,

HDU 6069 Counting Divisors(唯一分解定理+因子数)

http://acm.hdu.edu.cn/showproblem.php?pid=6069 题意: 思路: 根据唯一分解定理,$n={a_{1}}^{p1}*{a2_{}}^{p2}...*{a_{m}}^{pm}$,那么n的因子数就是 n的k次方也是一样的,也就是p前面乘个k就可以了. 先打个1e6范围的素数表,然后枚举每个素数,在[ l , r ]寻找该素数的倍数,将其分解质因数. 到最后如果一个数没有变成1,那就说明这个数是大于1e6的质数.(它就只有0和1两种选择) 1 #includ

hdu6069 多校Counting Divisors

思路:对于n^k其实就是每个因子的个数乘了一个K.然后现在就变成了求每个数的每个质因子有多少个,但是比赛的时候只想到sqrt(n)的分解方法,总复杂度爆炸,就一直没过去,然后赛后看官方题解感觉好妙啊!通过类似素数筛法的方式,把L - R的质因子给分解,就可以在O(nlogn)的时间之内把所以的数给筛出来. /* gyt Live up to every day */ #include<cstdio> #include<cmath> #include<iostream>