校内互测 数学作业 (线性筛求约数个数)

数学作业
1.1 题目描述Description
又是一节晚自习,jn 像往常一样流连于梦乡,但突然被同桌的你戳醒时,却发现数学老师正
笑呵呵的站在他面前,“拿出你的数学作业,我要检查”,jn 自然不可能在梦中写作业了,
然后喜(sang)闻(xin)乐(bing)见(kuang)的事情发生了,数学老师呵呵一笑,“你不用做今晚数
学作业了,不是搞 oi 么,给我算这个吧:
Fibonacci 数列是这样一个数列:
F1 = 1, F2 = 1, F3 = 2 . . .
Fi = Fi-1 + Fi-2 (当 i >= 3)
对于某一个 Fibonacci 数 Fi,有多少个 Fj 能够整除 Fi (j 可以大于等于 i)  ps:a能整除b, b mod a==0
共有 m 组询问,输出每个问题答案的和 ans,为了方便输入,n[i]=(n[i-1]*a+b)mod c
jn 自然不会做了,所以找到你来帮助他
1.2 输入Input
从 math.in 输入第一行有五个正整数 m,n1,a,b,c
1.3 输出Output
输出到 math.out
1.4 样例输入SampleInput
2 2 2 1 8
1.5 样例输出SampleOutput
6
1.6 数据范围Hint
对于 30%的数据,t≤10,n,a,b,c≤1000 对于 100%的数据,t≤3000000 n,a,b,c≤1000000


题解:根据学长所说,这是bzoj上某道关于Fibonacci数列的权限题的变形。

这道题乍一看得出结论不可做,因为Fibonacci数列的结果不到第100个就爆long long 了,那么面对这么一道神题该怎么做呢?    打表找规律啊!

打出的表如下:

1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 
2 3 6 9 12 15 18 21 24 27 30 33 36 39 42 45 48 51 54 57 60 63 66 69 72 75 78 81 84 87 90 93 96 99 
3 4 8 12 16 20 24 28 32 36 40 44 48 52 56 60 64 68 72 76 80 84 88 92 96 98 
5 5 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 85 90 
8 6 12 18 24 30 36 42 48 54 60 66 72 78 84 90 96 
13 7 14 21 28 35 42 49 56 63 70 77 84 91 94 
21 8 16 24 32 40 48 56 64 72 80 88 
34 9 18 27 36 45 54 63 72 81 90 
55 10 20 30 40 50 60 70 80 90 
89 11 22 33 44 55 66 77 88 
144 12 24 36 48 60 72 84 
233 13 26 39 52 65 78 91 
377 14 28 42 56 70 84 
610 15 30 45 60 75 90 
987 16 32 48 64 80 
1597 17 34 51 68 85 
2584 18 36 54 72 90 
4181 19 38 57 76 
6765 20 40 60 80 
10946 21 42 63 84 

17711 22 44 66 88 

28657 23 46 69 92 
(ps:其中蓝色的表示Fibonacci数列,后面的一串数表示当前这个位置的数能整除数列中的那些数的编号,当然只体现了前100)

这样看规律是否一目了然了?也就是每个数在数列中下标的约数个数即为有多少个 Fj 能够整除 Fi (j 可以大于等于 i) 的答案,当然有两个特例,那就是第一个数满足条件的个数有两个,因为第二个数也为一,再者因为第二个数为1,所以无论是不是二的倍数都可以整除,所以数列中下标为奇数的答案都需要加1.


其他就没什么好说的了,直接看代码吧。

测试时不会用线性筛求约数个数,所以打了一个分解质因数的暴力,结果过了9组,TLE了一组,貌似时间复杂度不是很科学。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
int m,n,a,b,c,num;
int f[1000003],pd[1000003];
int ans[1000003];
long long sum,k,n1;
void shai()//线性筛求素数
{
  f[1]=1; num=0;
  for (int i=2;i<=1000000;i++)
   {
   	 if (!f[i])  num++,pd[num]=i;
   	 for(int j=1;j<=num;j++)
   	  if (i*pd[j]<=1000000)
   	   f[i*pd[j]]=1;
   	  else
   	   break;
   }
}
int main()
{
  freopen("math.in","r",stdin);
  freopen("math.out","w",stdout);
  scanf("%d%d%d%d%d",&m,&n,&a,&b,&c);
  shai();
  //for (int i=1;i<=num;i++)
  // cout<<pd[i]<<endl;
  n1=n;
  for (int i=1;i<=m;i++)
   {
   	int l=0; long long t=n1;
   	if (f[t])
   	 for (int j=1;j<=n1;j++)//分解质因数的过程
   	 {
   	 	if (t%pd[j]==0)
   	 	 {
   	 	   l++; int s=0;
   	 	   while (t%pd[j]==0)
   	 	    t/=pd[j],s++;
   	 	   ans[l]=s;
   	 	 }
   	 	if (t==1)  break;
   	 	if (!f[t]) //我觉得这里的特判对优化时间很有帮助 
   	 	 {
   	 	   l++; ans[l]=1; break;
   	 	 }
   	 }
   	else
   	 {
   	    l++; ans[l]=1;
   	 }
   	k=1;
   	for (int j=1;j<=l;j++)
   	 k*=(ans[j]+1);
   	if (n1%2!=0)  k++;
   	if (n1==1)  k==2;
   	//cout<<n1<<" "<<k<<endl;
   	sum+=k;
   	n1=(long long)(n1%c*a%c+b%c)%c+1;
   }
  printf("%I64d\n",sum);
}

AC代码:线性筛求约数个数

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int d[1100000],t[1000100],p[1000010],prime[1000010],num,n,m,a,b,c;
long long sum,n1;
void get_d()//线性筛求约数个数
{
  p[1]=1; d[1]=2; t[1]=1;
  for (int i=2;i<=1000000;i++)
   {
   	 if (!p[i])
   	  {
   	  	d[i]=2,t[i]=1;
        num++; prime[num]=i;
      }
     for (int j=1;j<=num;j++)
      {
      	if (prime[j]*i>1000000) break;
      	p[i*prime[j]]=1;
      	if (i%prime[j]==0)
      	 {
      	 	t[i*prime[j]]=t[i]+1;
      	 	d[i*prime[j]]=d[i]/(t[i]+1)*(t[i*prime[j]]+1);
      	 	break;
      	 }
      	else
      	 {
      	 	d[i*prime[j]]=d[i]*2;
      	 	t[i*prime[j]]=1;
      	 }
      }
   }
}
int main()
{
  freopen("math.in","r",stdin);
  freopen("math.out","w",stdout);
  scanf("%d%d%d%d%d",&m,&n,&a,&b,&c);
  get_d();
  n1=n;
  for (int i=1;i<=m;i++)
   {
   	 sum+=d[n1];
   	 if (n1%2!=0&&n1!=1)  sum++;
   	 n1=(long long)(n1%c*a%c+b%c)%c+1;
   }
  printf("%I64d\n",sum);
}

线性筛求约数个数过程的个人理解:

d函数
额外记录最小质因子的指数t(i) 
I:质数

d[i]=2,t[i]=1;//这个因为质数的定义
II:最小质因子质数不为1 
t[i*prime[j]]=t[i]+1;//因为线性筛是利用它最小的质因子来筛,所以如果i % prime[j]==0也就意味着最小质因子的指数又加了1
d[i*prime[j]]=d[i]/(t[i]+1)*(t[i*prime[j]]+1);//这个是因为质因子个数的计算方法
III:最小质因子指数为1 
d[i*prime[j]]=d[i]*2;//加入了一个最小质因子,那就是在组合因子是多了2种选择
t[i*prime[j]]=1;


你可能感兴趣的:(校内互测 数学作业 (线性筛求约数个数))