51nod 1847 奇怪的数学题

51nod 1847 奇怪的数学题

原题链接:
https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1847

定义:

sgcd(a,b) a b的次大公约数

f(a) a 的次大约数
sgcd(a,b)=f(gcd(a,b))

特别的: f(1)=0

题目要求计算:
i=1nj=1nf(gcd(i,j))k

推导:

i=1nj=1nf(gcd(i,j))=d=1nf(d)ki=1nj=1n[gcd(i,j)=d]=d=1nf(d)ki=1ndj=1nd[ij]=d=1nf(d)k(1+2i=1ndφ(i))

如果可以分段计算 f 的前缀和与φ的前缀和。就可以快速计算答案。

对于 φ 有:
φid0=id

经典杜教筛。

对于计算:
Sf(n)=i=1nf(i)

定义: C(t,n) 为, [1,n] 上不可以被前t个素数整除的数字的 k 次幂之和

对于第t个素数 Pt [1,n] 上最小素因子为 Pt 的数字必然有:

. 这些数是 Pt 的倍数

. 这些数不能被前 t1 个素数整除

所以:最小素因子为 Pt 的数字 k 次幂之和为:
C(t1,nPt)Pkt

所以:
C(t,n)=C(t1,n)C(t1,nPt)PktSf(n)=t1C(t1,nPt)

定义:
S(t,n)=S(t1,n)+C(t1,nPt)

S(0,n)=0

所以:
Sf(n)=S(π(n),n)

显然当 Pt+1>n:
C(t,n)=1

P2t>n 时:
C(t,n)=C(t1,n)Pkt

那么为们只更新
C(t1,nPt)>1

这个情况。多开一个数组记录最早出现 1 的哪个素数标号,预处理合适的前若干个素数k次幂之和。

总时间复杂度:
T(n)=1<Pnn(O(nP)+O(P))=O(n43log n)

具体方法类似于:http://blog.csdn.net/ZLH_HHHH/article/details/78020896

下面是AC代码:

#include <algorithm>
#include <string.h>
#include <stdio.h>
#include <cmath>
#define MAXN 40000
#define LIM 1000000
using namespace std;

typedef  unsigned int uint;

uint size;
const uint INF=0x3f3f3f3f;
struct mat
{
    uint A[53][53];

    mat()
    {
        memset(A,0,sizeof A);
    }

    void C()
    {
        A[0][0]=1;
        for(uint i=1;i<size;i++)
        {
            A[i][0]=1;
            for(uint j=1;j<=i;j++)  A[i][j]=A[i-1][j]+A[i-1][j-1];
        }
        for(uint i=0;i<size;i++) A[size][i]=A[size-1][i];
        A[size][size]=1;
    }

    mat operator *(const mat &a)const
    {
        mat b;
        for(uint i=0;i<=size;i++)
            for(uint j=0;j<=size;j++)
                for(uint k=0;k<=size;k++)
                    b.A[i][j]+=A[i][k]*a.A[k][j];
        return b;
    }
}bit[33];

uint B[MAXN];
uint b[MAXN];
uint Sp[LIM];
uint _Sp[LIM];
uint C[MAXN];
uint c[MAXN];
uint Ik[MAXN];
uint prim[MAXN];
uint S[MAXN];
uint s[MAXN];
uint tmp[MAXN];
uint pai[MAXN];
uint paik[MAXN];
uint f[MAXN];
uint _TMP[55],TMP[55];
uint test[100];
bool vis[MAXN];
uint Pow(uint a,uint b)
{
    uint tmp=1;
    while(b)
    {
        if(b&1)
            tmp=tmp*a;
        a=a*a;
        b>>=1;
    }
    return tmp;
}
uint clat_Sk(uint n)
{
    n--;
    uint k=0;
    for(uint i=0;i<=size;i++)TMP[i]=1;
    while(n)
    {
        if(n&1)
        {
            memset(_TMP,0,sizeof _TMP);
            for(uint i=0;i<=size;i++)
                for(uint j=0;j<=size;j++)
                    _TMP[i]+=bit[k].A[i][j]*TMP[j];
            for(uint i=0;i<=size;i++)TMP[i]=_TMP[i];
        }
        n>>=1;
        k++;
    }
    return TMP[size];
}
uint clat_S(uint n)
{
    if(n&1)return n*((n+1)>>1);
    return (n>>1)*(n+1);
}
void clat_Sp(uint n,uint d)
{
    uint m=sqrt(n)+1,ans=0;
    for(uint L=1;L<m;L++)   ans+=(n/L-n/(L+1))*Sp[L];
    for(uint i=n/m;i>1;i--)
    {
        uint u=n/i;
        if(u<LIM)
            u=Sp[u];
        else
            u=_Sp[d*i];
        ans+=u;
    }
    _Sp[d]=clat_S(n)-ans;
}
void slove(uint n)
{
    if(n<LIM)return;
    for(uint i=n/LIM;i;i--)clat_Sp(n/i,i);
}
uint gcd(uint a,uint b)
{
    if(b==0)return a;
    return gcd(b,a%b);
}
uint DFS(uint t,uint n)
{
    if(t==0)return n>0?clat_Sk(n):0;
    return DFS(t-1,n)-DFS(t-1,n/prim[t])*prim[t];
}
int main ()
{
    uint n,K,m,lim,X,fir;
    scanf("%u %u",&n,&K); size=K+1;
    m=sqrt(n)+1;    lim=pow(n,0.25)+1;  X=m;
    bit[0].C();
    for(uint i=1;i<33;i++) bit[i]=bit[i-1]*bit[i-1];
    for(uint i=1;i<MAXN;i++)Ik[i]=Pow(i,K);
    for(uint i=1;i<=m;i++)
    {
        b[i]=i;
        B[i]=n/i;
        c[i]=c[i-1]+Ik[i];
        C[i]=clat_Sk(n/i);
    }
    for(uint i=2;i<MAXN;i++)
    {
        pai[i]=pai[i-1];
        paik[i]=paik[i-1];
        if(vis[i])continue;
        prim[++pai[i]]=i;
        paik[i]+=Ik[i];
        for(uint j=i<<1;j<MAXN;j+=i)   vis[j]=true;
    }
    uint k=1;
    while(prim[k]<lim)
    {
        for(uint i=1;i<m;i++)
        {
            uint t=prim[k]*i;
            uint u=n/t;
            uint bu;
            if(u<X)
            {
                bu=b[u];
                u=c[u];
            }
            else
            {
                u=C[t];
                bu=B[t];
            }
            B[i]-=bu;
            S[i]+=u;
            C[i]-=u*Ik[prim[k]];
        }
        for(uint i=X-1;i;i--)
        {
            b[i]-=b[i/prim[k]];
            s[i]+=c[i/prim[k]];
            c[i]-=c[i/prim[k]]*Ik[prim[k]];
        }
        k++;
    }
    for(uint i=prim[k-1]+1;i<m;i++)s[i]+=pai[i]-k+1;
    lim=m;
    memset(tmp,0x3f,sizeof tmp);
    while(prim[k]<m)
    {
        fir=lim;
        lim=n/(prim[k]*prim[k])+1;
        for(uint i=1;i<lim;i++)
        {
            uint t=i*prim[k];
            uint u=n/t,bu;
            if(u<X)
            {
                if(u<prim[k])   u=bu=1;
                else
                {
                    bu=pai[u]-k+2;
                    u=paik[u]-paik[prim[k-1]]+1;
                }
            }
            else
            {
                if(tmp[t]!=INF)
                {
                    u=C[t]-paik[prim[k-1]]+paik[prim[tmp[t]-1]];
                    bu=B[t]-k+tmp[t];
                }
                else
                {
                    u=C[t];
                    bu=B[t];
                }
            }
            S[i]+=u;
            B[i]-=bu;
            C[i]-=u*Ik[prim[k]];
        }
        for(uint i=lim;i<fir;i++)tmp[i]=k;
        k++;
    }
    for(uint i=1;i<m;i++)
        if(tmp[i]<INF)
        {
            S[i]+=k-tmp[i]-1;
            B[i]-=k-tmp[i]-1;
        }
    for(uint i=1;i<m;i++) S[i]+=B[i]-1;
    memset(vis,0,sizeof vis);
    for(uint i=2;i<MAXN;i++)
    {
        if(i<100)test[i]=test[i-1]+f[i];
        if(vis[i])continue;
        for(uint j=i,t=1;j<MAXN;j+=i,t++)
        {
            if(vis[j])continue;
            vis[j]=true;
            f[j]=Ik[t];
        }
        if(i<100)test[i]+=f[i];
    }
    for(uint i=1;i<LIM;i++)
    {
        Sp[i]=i-Sp[i];
        for(uint j=i<<1;j<LIM;j+=i)Sp[j]+=Sp[i];
        Sp[i]+=Sp[i-1];
    }
    slove(n);

    uint ans=0;

    for(uint L=1;L<m;L++)
    {
        uint u2;
        if(L+1==m)
            u2=s[n/(L+1)];
        else
            u2=S[L+1];
        ans+=(S[L]-u2)*(Sp[L]*2-1);
    }
    for(uint d=n/m;d>1;d--)
    {
        uint u=n/d;
        if(u<LIM)
            u=Sp[u];
        else
            u=_Sp[d];
        ans+=f[d]*(2*u-1);
    }
    printf("%u\n",ans);
    return 0;
}
/*
 938393691 46
 1106647490
 */
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值