【欧拉筛/线性筛】BZOJ2813 奇妙的Fibonacci

本文探讨了一道涉及数论的BZOJ2813题目,重点讲解了如何利用欧拉筛处理斐波那契数列的因子性质。通过公式和算法解释,展示了如何在筛素数过程中统计每个数的因子信息,并特别指出在奇数情况下需要注意的特殊情况。
摘要由CSDN通过智能技术生成

题面在这里

一道很好的数论题……让人思考很多……

参考博客:斐波那契数列的性质
首先有这个公式:

n|mfn|fm

那么题目就转化为:询问i有多少个因子,以及所有因子的平方和

可以用欧拉筛一边处理一边统计:
ei 表示i的最小质因数的次数
di 表示i除去最小质因数后的数
gi 表示i的因子个数
fi 表示i的因子的平方和

在筛素数的时候:
ipj=k

1.若 i%pj>0 ,说明i之前没有 pj 这个因子
根据欧拉筛的性质, pj 一定是k最小的质因数
那么:
ek=1,dk=i
gk=2g[i]
fk=p2jf+fi
(把因数分成含 pj 和不含 pj 的来考虑)

2.若 i%pj=0 ,说明i之前已经出现过 pj 这个因子
同样,把 k 的因数看为含pj和不含 pj 两类考虑,可得:
ek=ei+1,dk=di
gk=gi/(ei+1)(ek+1)
fk=fip2j+fdi

但是要注意:
n|mfn|fm
这个性质有一个特殊情况,就是 f2=1
如果询问的i是奇数,以上算法会认为 f2 不是 fi 的因子
那么统计答案的时候特判一下即可

附上代码:

#include<cstdio>
#define LL long long
const int maxn=10000005,tt=1000000007;
int q,now,A,B,C,sumg,sumf;
int e[maxn],d[maxn],g[maxn],f[maxn],p[maxn];
bool vis[maxn];
LL sqr(int x) {return (LL)x*x;}
int main(){
    scanf("%d%d%d%d%d",&q,&now,&A,&B,&C);
    d[1]=g[1]=f[1]=1;
    for (int i=2;i<=C;i++){
        if (!vis[i])
         p[++p[0]]=i,
         e[i]=d[i]=1,g[i]=2,
         f[i]=(sqr(i)+1)%tt;
        for (int j=1,k;j<=p[0]&&(k=i*p[j])<=C;j++){
            vis[k]=1;
            if (i%p[j]>0)
             e[k]=1,d[k]=i,
             g[k]=(g[i]+g[i])%tt,
             f[k]=(sqr(p[j])+1)*f[i]%tt;
            else{
                e[k]=e[i]+1,d[k]=d[i],
                g[k]=(LL)g[i]/(e[i]+1)*(e[k]+1)%tt,
                f[k]=(f[i]*sqr(p[j])+f[d[i]])%tt;
                break;
            }
        }
    }
    sumg=sumf=0;
    while (q--){
        (sumg+=g[now]+(now&1))%=tt;
        (sumf+=f[now]+4*(now&1))%=tt;
        now=((LL)now*A+B)%C+1;
    }
    printf("%d\n%d",sumg,sumf);
    return 0;
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值