[bzoj2813]奇妙的Fibonacci数列 (数论+线性筛)

题目:

我是超链接

题意:

定义Fibonacci数列为 F1=1 , F2=1,Fi=Fi1+Fi2(i>=3)
对于某一个数Fi,求有多少个Fj能够整除Fi (i可以等于j),以及所有满足条件的j的平方之和。
询问次数 3106 i107

题解:

这里有Fibonacci的几个柿子,用F表示:

gcd(Fi,Fi+1)=gcd(Fi1,Fi)=...=gcd(F1,F2)=1

Fn+m=Fn1Fm+FnFm+1

gcd(Fn+m,Fn)=gcd(Fn1Fm+FnFm+1,Fn)=gcd(Fn1Fm,Fn)=gcd(Fm,Fn)

我们归纳出来的结果就是
gcd(Fi,Fj)=Fgcd(i,j)

那么对于这道题Fj | Fi等价于j|i。
对于每个询问直接算因子明显会超时,考虑用线性筛法来做,埃式筛法复杂度为O(nloglogn),欧式筛法复杂度为O(n),这里使用的是欧拉筛法。
记e[i]为i的最小质因数次数,d[i]为i除去最小质因数的数,g[i]为i的因数个数,f[i]为i的因数平方和。
设当前枚举到i,质数枚举到prime[j],记A=i*prime[j]。
分类讨论:

1、 i mod prime[j] !=0
这时prime[j]是A的最小质因数且为1次
分析A的因数的组成,可以知道他由两部分组成,
一部分是i的所有因数 另一部分是i的所有因数各乘上prime[j]得到的

因此
g[A]=g[i]2
f[A]=f[i](prime[j]2+1)
d[A]=i

2、 i mod prime[j] ==0
此时prime[j]是A的最小质因数 为e[i]+1次
d[A]=d[i]
令B=i/(prime[j]^e[i]) 即B是i除去最小质因数后得到的数d[i]

f[i]=f[B]e[i]k=0prime[j]2k

f[A]=f[B]e[i]+1k=0prime[j]2k

消去f[i]即得 f[A]=f[i](prime[j]2)+f[B]
这里涉及到对于给定的i,B=i/(prime[j]^e[i])的计算,记为d[i]。
至此,我们得到了整个问题的O(n)预处理算法。

因为f[2]=1,所以如果q是奇数约数和要+1,平方和要+4
注意:q不是小于1e7的,ta可能等于1e7!预处理的时候一定要处理C的情况啊

代码:

#include <cstdio>
#define LL long long
using namespace std;
const int CC=1e7;
const int mod=1e9+7;
int q,C,e[CC+5],d[CC+5],g[CC+5],f[CC+5],prime[CC+5],num;
bool ss[CC+5];
//e[i]表示最小质因子次数,d[i]表示除去最小质因子之后的数
//g[i]表示含有约数个数,f[i]表示j的平方之和 
void pre()
{
    g[1]=1; f[1]=1;
    for (int i=2;i<=C;i++)
    {
        if (!ss[i])
        {
            prime[++num]=i; e[i]=1; d[i]=1; g[i]=2; f[i]=(LL)i*i%mod+1;
        }
        for (int j=1;j<=num && prime[j]*i<=C;j++)
        {
            int A=prime[j]*i;
            ss[A]=1;
            if (i%prime[j]!=0)
            {
                e[A]=1; d[A]=i; g[A]=2*g[i]; f[A]=f[i]*((LL)prime[j]*prime[j]%mod+1)%mod;
            }
            else
            {
                e[A]=e[i]+1; d[A]=d[i]; g[A]=(g[i]/e[A])*(e[A]+1); 
                f[A]=((LL)f[i]*prime[j]%mod*prime[j]%mod+f[d[i]])%mod;
            }
        }
    }
}
int main()
{
    int Q,i,A,B,ans1=0,ans2=0;
    scanf("%d%d%d%d%d",&Q,&q,&A,&B,&C);
    pre();
    A%=C; B%=C;
    for (i=1;i<=Q;i++) 
    {
        if (i>1) q=((LL)q*A+B)%C+1;
        ans1=(ans1+g[q]+(q&1))%mod; 
        ans2=(ans2+f[q]+(q&1)*4)%mod;
    }
    printf("%d\n%d",ans1,ans2);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值