不可表示的数(庞果网)完全解答

来源:互联网 发布:淘宝店铺图片上传视频 编辑:程序博客网 时间:2024/06/05 17:46

题目详情:

给定表达式[x/2] + y + x * y, 其中x,y都是正整数。其中的中括号表示下取整,例如[3/2] = 1 , [5/2]  = 2。

有些正整数可以用上述表达式表达出来,例如正整数2,当取x = y = 1时,可以把2表达出来

( 解释下:当x=y=1时, [x / 2] + y + x * y = [1 / 2] + 1 + 1 * 1 = 0+1+1 = 2 );

有些数可以有多种方式表达,例如13可以由 x = 2 y = 4 以及x = 3 y = 3来表示;

有些数无法用这个表达式表达出来,比如3。

从1开始第n个不能用这个表达式表示出来的数,我们叫做an,例如a1=1 a2=3,给定n,求an。


输入:n值 1<=n<=40

输出:an % 1000000007的结果(因为结果较大,输出an %1000000007的结果)。


方法一:

算法思想:

将要表示的数记为count,则count = [x/2] + y + x * y

将式子变形count = (x + 1) * y + x / 2  ↔  y = (count  -  x/2 )  /  (x + 1) ①

所以当我们对count取不同值时,判断是否可以被表示,即判断①式中y  对于 x取(1、2、3…(x / 2 + x + 1 <= count)

    能否得到整数值( (count  -  x/2 )  %  (x + 1) == 0),若对于所有x, y均得不到整数,则count不可表示

当x取1时,count = 2 * y, 所以不可表示的数一定是奇数

所以我们将count从1开始以2为增补量不断测试,得到第n个不可表示的数就输出


代码如下:

#include <stdio.h>int givean(int n) {    int i = 0, m;int count = 1, x;    do    {              m = 1;        for (x = 1; x / 2 + x + 1<= count; x++)        {if ((count - x/2) % (x + 1) == 0){m = 0;break;}        }//当count不可表示if (m == 1)i++;count += 2;}while (i < n);return (count - 2)% 1000000007;}int main(){        int n, x;    scanf("%d", &n);    x = givean(n);    printf("%d\n", x);    return 0;}

但是当我们运行时发现得到a1 = 1, a2 = 3, a3 = 15, a4 = 63, a5 = 4095, a6 = 65535, a7 = 262143很快。

当n = 8,算出(a8 = 1073741823) % 1000000007= 73741816却要两、三分钟。所以此方法理论是对的,却不合适。

相信很多人像我一样在,接下来是对上面算法进行各种优化,加大count的增补量、减小x的循环范围等等,但作用对于求解a40来说将微乎其微。

郁闷,疑惑之中在网上大查一番终于有所眉目,将借鉴整理如下。


方法二

下面仁兄博客将对此方法用到的数学方法,进行详细说明:

http://blog.csdn.net/oucyxc/article/details/9958035

相信看完以上博客我们知道:若一个数2^(m + 1)- 1是素数,则2^m - 1即为一个不可表示的数(m >= 1)

其实我们把2^p - 1为素数的数叫做梅森素数(百度可查)且对p的取值依次为:

2,3,5,7,13,17,19,31,61,89,
107,127,521,607,1279,2203,2281,3217,4253,4423,
9689,9941,11213,19937,21701,23209,44497,86243,110503,132049,
216091,756839,859433,1257787,1398269,2976221,3021377,6972593,13466917,2099601
1,

24036583,25964951,30402457,32582657,37156667,42643801,43112609,57885161

目前只发现48个梅森素数。


编程的思想是我们可以把前四十个p值存在一个数组里,比如求第n个不可表示的数,则取第n个p值(假设为k),

计算第n个不可表示的数(2^(k - 1) - 1)% 1000000007 ↔ 2^(k - 1) % 1000000007 - 1,

但是我们会遇到一个问题2^(k - 1) 肯定会出现溢出.


这时我们可以,利用一个公式(A * B)mod C = ((A mod C) * (B mod C)) mod C

可推(a * b * c * d)mod e(若b, c, d是小于c的)= (((a mod e)* b) mod e * c) mod e * d) mod e

可推2^m mod c = ( (2^(m - 1) mod c)  *2) mod c = ((2^(m - 2) mod c   *  2) mod c   *   2) mod c(即乘以一次二取一次摸,多少次方重复多少次)

所以我们边取摸边计算就不会溢出


 代码如下:

#include <stdio.h>int givean(int n){int p[]={    2,3,5,7,13,17,19,31,61,89,107,127,521,607,1279,2203,2281,3217,4253,4423,9689,9941,11213,19937,21701,23209,44497,86243,110503,132049,216091,756839,859433,1257787,1398269,2976221,3021377,6972593,13466917,20996011};int num = p[n-1] - 1, s = 1, i;for (i = 1; i <= num; i++){s *= 2;s = s % 1000000007;}return s-1;}int main(){int n;scanf("%d", &n);printf("%d\n", givean(n));    return 0;}



原创粉丝点击