BZOJ 4176 Lucas的数论

来源:互联网 发布:知乎扒皮密子君催吐 编辑:程序博客网 时间:2024/06/04 19:54

BZOJ 4176 Lucas的数论

原题链接:
http://www.lydsy.com/JudgeOnline/problem.php?id=4176

题目是要求计算:

i=1nj=1nd|ij1

设:

nm=i=1rPxi+yii

其中:

n=i=1rPxii

m=i=1rPyii

则对于:

f(nm)=d|nm1=k=1rcPxk+ykkdnmPxk+ykk1=k=1rdnmPxk+ykkcPxk+ykk1=k=1rdnmPxk+ykkaPxkkbPykk[ab]=k=1raPxkkbPykk[ab]=a|nb|m[ab]

那么:
answer=i=1nj=1na|ib|j[ab]=a=1nb=1na|i,inb|j,jn[ab]

令:

F(k)=a=1nb=1na|i,inb|j,jn[k gcd(a,b)]=a=1nkb=1nka|i,inkb|j,jnk1=(i=1nknki)2

记:
S(n)=(i=1nni)2

则:

answer=i=1nμ(i)S(ni)

m=nM为梅藤斯函数

answer=L=1mS(L)(M(nL)M(nL+1))+i=1nm+1μ(i)S(ni)

显然计算S(n)复杂度是O(n)

而对于梅藤斯函数

因为μI=e

直接套用公式有:

M(n)=1i=2nM(ni)

注:对这一步有问题的可以参阅链接 :http://blog.csdn.net/ZLH_HHHH/article/details/77836062

进而有:

M(n)=1L=1mM(L)(nLnL+1)+i=2nm+1M(ni)

m不要取太大。BZOJ程序是逐个运行后算总时间的。。。

如果m=n23,计算M(n)的时间为:

i=1n13O(ni)=O(n23)

总时间复杂度:

T(n)=i=1n(2O(i)+O((ni)23))=O(n56)O(107.5)

实际上,m略小反而更快。

下面是代码:

#include <algorithm>#include <string.h>#include <cmath>#include <stdio.h>#define MAXN 1000009using namespace std;typedef long long LL;const int P=1e9+7;const int P_=P-1;int mu[100009];int tmpm[MAXN]={0,-1};int tmp[100009];int ML[100009];void slove(int n,int d){    int ans=0,m=(int)sqrt(n)+1;    for(int L=1;L<m;L++)    {        int u=n/L-n/(L+1);        ans+=(LL)tmpm[L]*u%P;        if(ans>P_)ans-=P;    }    for(int i=n/m;i>1;i--)    {        int u=n/i;        if(u<MAXN)            ans+=tmpm[u];        else            ans+=tmp[i*d];        if(ans>P_)ans-=P;    }    tmp[d]=(P+1-ans)%P;}int M(int n){    if(n<MAXN)return tmpm[n];    for(int i=n/MAXN; i ; i--)    slove(n/i,i);    return tmp[1];}int S(int n){    int ans=0,m=(int)(sqrt((double)n)+0.0001)+1;    for(int L=1;L<m;L++)    {        int u=n/L-n/(L+1);        ans+=(LL)L*u%P;        if(ans>P_)ans-=P;    }    for(int i=n/m;i;i--)    {        ans+=n/i;        if(ans>P_)ans-=P;    }    ans=(LL)ans*ans%P;    return ans;}void init(){    for(int i=1;i<MAXN;i++)    {        tmpm[i]=-tmpm[i];        if(i<100000)mu[i]=tmpm[i];        for(int j=i<<1;j<MAXN;j+=i) tmpm[j]+=tmpm[i];        tmpm[i]+=tmpm[i-1];        if(tmpm[i]<0)tmpm[i]+=P;        if(tmpm[i]>P_)tmpm[i]-=P;    }}int main (){    init();    int n;    scanf("%d",&n);    int m=(int)sqrt((double)n)+1,ans=0;    ML[1]=M(n);    for(int L=1;L<m;L++)    {        ML[L+1]=M(n/(L+1));        ans+=(LL)S(L)*(ML[L]-ML[L+1]+P)%P;        if(ans>P_)ans-=P;    }    for(int i=n/m;i;i--)    {        ans+=(LL)mu[i]*S(n/i);        if(ans>P_)ans-=P;        if(ans<0)ans+=P;    }    printf("%d\n",ans);    return 0;}