[BZOJ2813]奇妙的Fibonacci(线性筛)

题目:

我是超链接

题解:

通过打表找到的规律我们发现 Fj|Fij|i F j | F i 等 价 于 j | i (除了f[2]=1的2之外,这个最后特判就可以)

这个题目就相当于问约数个数和和约数平方和,这两个函数都是可以线筛出来的

首先是约数个数和,如果一个数 a=pk11pk22pk33.... a = p 1 k 1 p 2 k 2 p 3 k 3 . . . . 那么他的约数个数是 (k1+1)(k2+1)... ( k 1 + 1 ) ∗ ( k 2 + 1 ) ∗ . . . ,这个不难考虑

d(i)表示i的约数个数,e(i)表示最小质因数的指数,那么线筛三步走

当i是个质数时,d(i)=2,e(i)=1

当i不是个质数,枚举质数p,由i转移过去,再分类
p/|i p ⧸ | i ,这个质数第一次出现,那么 e(ip)=1d(ip)=d(i)2=d(i)d(pri(j)) e ( i p ) = 1 , d ( i p ) = d ( i ) ∗ 2 = d ( i ) ∗ d ( p r i ( j ) )

p|i p | i ,即以前出现过这个质数, e(ip)=e(i)+1d(ip)=d(i)/(e(i)+1)(e(i)+2) e ( i p ) = e ( i ) + 1 , d ( i p ) = d ( i ) / ( e ( i ) + 1 ) ∗ ( e ( i ) + 2 ) ,就是除以这个质数出现的次数+1然后更改为新的次数

<script type="math/tex; mode=display" id="MathJax-Element-1419"></script>
然后是第二问约数平方和
g(i)表示约数平方和,h(i)表示排除最小质因数部分的约数平方和,依然线筛三步走

当i是个质数时, g(i)=i2+1h(i)=1 g ( i ) = i 2 + 1 , h ( i ) = 1

当i不是个质数,枚举质数p,由i转移过去,再分类
p/|i p ⧸ | i ,这个质数第一次出现,那么他本来就不含这个质数,可以给h(ip)赋为i,加上这一个质数之后,整个约数的平方和会增加上这个pri,那么g(ip)就是g(i)+pri*pri*g(i)

p|i p | i ,即以前出现过这个质数,那么我把以前不含这个质数的平方和再加上有了这一群一样的质数之后的平方和,显然h(i)中一定不含有带pri的约数,所以可以先用i中所有约数乘上pri构造出含有pri的约数,再接收g(h(i))来累加所有不含pri的约数

代码:

#include <cstdio>
#define LL long long
using namespace std;
const int mod=1e9+7;
const int maxn=10000000;
const int N=10000005;
int tot,e[N],h[N];bool ss[N];
LL pri[N],d[N],g[N];
void init(int n)
{
    d[1]=g[1]=1;
    for (int i=2;i<=n;i++)
    {
        if (!ss[i]) 
        {
            pri[++tot]=i; d[i]=2; e[i]=1;
            g[i]=((LL)i*i)%mod+1; h[i]=1;
        }
        for (int j=1;j<=tot&&pri[j]*i<=n;j++)
        {
            ss[pri[j]*i]=1;
            if (i%pri[j]==0)
            {
                d[i*pri[j]]=d[i]/(e[i]+1)*(LL)(e[i]+2)%mod;
                e[i*pri[j]]=e[i]+1;
                g[i*pri[j]]=(g[h[i]]+(LL)g[i]*pri[j]%mod*pri[j]%mod)%mod;
                h[i*pri[j]]=h[i]%mod;
                break;
            }
            d[i*pri[j]]=d[i]*(LL)2%mod;
            e[i*pri[j]]=1;
            g[i*pri[j]]=(g[i]+(LL)pri[j]*(LL)pri[j]%mod*g[i]%mod)%mod;
            h[i*pri[j]]=i;
        }
    }
}
int main()
{
    int Q,q,a,b,c;
    scanf("%d%d%d%d%d",&Q,&q,&a,&b,&c);
    init(c);LL A=0,B=0;
    for (int t=1;t<=Q;t++)
    {
        A=(A+d[q])%mod;
        if (q&1) A=(A+1)%mod;
        B=(B+g[q])%mod;
        if (q&1) B=(B+4)%mod;
        q=((LL)q*(LL)a%c+b)%c+1;    
    }
    printf("%lld\n%lld",A,B);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值