BZOJ 2813 奇妙的Fibonacci

题目:http://www.lydsy.com/JudgeOnline/problem.php?id=2813

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

题解:
由定义不难得到

gcd(fibi,fibi+1)=gcd(fibi1,fibi)=...=gcd(fib1,fib2)=1
以及
fibn+m=fibn1fibm+fibnfibm+1

所以
gcd(fibn+m,fibn)=gcd(fibn1fibm,fibn)=gcd(fibn1,fibn)gcd(fibm,fibn)=gcd(fibm,fibn)

所以可以归纳得出
gcd(fibi,fibj)=fibgcd(i,j)

那么 fibj|fibi 等价于 j|i

对于每个询问直接算因子明显会超时,考虑用线性筛法来做,埃式筛法复杂度为 O(nloglogn) ,欧式筛法复杂度为 O(n) ,这里使用的是欧拉筛法。
e[i] i 的最小质因数次数,d[i] i 除去最小质因数的数,g[i] i 的因数个数,f[i] i 的因数平方和。
对于欧式筛中:

  • imodprime[j]>0 iprime[j] 第一次遇见最小质因数,因数个数乘2, f[iprime[j]] f[i]prime[j]2+f[i] (每个因子乘或不乘 prime[j] )。

    • imodprime[j]=0 prime[j] iprime[j] 的最小质因数但已经遇见过,最小质因数加1,因数个数关于最小质因数部分重算, d[iprime[j]] 不变, f[iprime[j]] f[i]prime[j]2+f[d[iprime[j]]]
    • 注意 fib1=fib2=1 ,这种情况并未算入某些奇数的答案中。
    • 代码:

      #include <cstdio>
      typedef long long LL;
      const int maxc = 664580, maxv = 10000001, mod = 1000000007;
      int cnt, prime[maxc], e[maxv], d[maxv], g[maxv], f[maxv], sumg, sumf;
      bool vis[maxv];
      int main()
      {
          int Q, q, A, B, C;
          scanf("%d%d%d%d%d", &Q, &q, &A, &B, &C);
          A %= C, B %= C;
          g[1] = f[1] = 1;
          for(int i = 2; i <= C; ++i)
          {
              if(!vis[i])
              {
                  prime[cnt++] = i;
                  e[i] = d[i] = 1;
                  g[i] = 2;
                  f[i] = ((LL)i * i + 1) % mod;
              }
              for(int j = 0, k; j < cnt && (k = i * prime[j]) <= C; ++j)
              {
                  vis[k] = 1;
                  if(i % prime[j] == 0)
                  {
                      e[k] = e[i] + 1;
                      g[k] = (g[i] / (e[i] + 1)) * (e[k] + 1);
                      d[k] = d[i];
                      f[k] = (f[i] * ((LL)prime[j] * prime[j] % mod) + f[d[i]]) % mod;
                      break;
                  }
                  else
                  {
                      e[k] = 1;
                      d[k] = i;
                      g[k] = g[i] << 1;
                      f[k] = f[i] * (((LL)prime[j] * prime[j] + 1) % mod) % mod;
                  }
              }
          }
          while(Q--)
          {
              sumg += g[q] + (q & 1);
              sumf += f[q] + 4 * (q & 1);
              if(sumg >= mod) sumg -= mod;
              if(sumf >= mod) sumf -= mod;
              q = (q * (LL)A + B) % C + 1;
          }
          printf("%d\n%d\n", sumg, sumf);
          return 0;
      }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值