• 信息学奥赛一本通 2082:【21NOIP提高组】报数 | 洛谷 P7960 [NOIP2021] 报数


    【题目链接】

    ybt 2082:【21NOIP提高组】报数
    洛谷 P7960 [NOIP2021] 报数

    【题目考点】

    1. 筛法求质数
    2. 因数
    3. 数字拆分
    4. 链表思想

    【解题思路】

    根据新的规则,任何一个十进制中某一位含有7的数字的倍数都不能报出来。
    那么不能报出来的数字一定存在某一位是7的因数。
    该题核心点为:如何判断一个数字的所有因数中,存在因数某一位是7。简单说成:判断某数字的因数中是否包含7。

    解法1:枚举(70分)

    has7函数:使用数字拆分的方法判断一个数字中是否有一位为7(是否包含7)。
    设函数fac7,fac7(n)返回值表示n的因数中是否包含7。

    • 先枚举出n的所有因数,可以枚举所有满足 i ≤ n i\le \sqrt{n} in 的i,如果n能整除i,那么i及n/i都是n的因数。
    • 用has7函数判断n的因数中是否包含7,如果存在包含的情况,那么fac7函数返回true。如果不存在包含7的情况,返回false。

    主函数中,对于每次输入的x,

    • 如果x的因数中包含7,那么输出-1。
    • 否则从x+1开始,不断判断后面的每个数字的因数中是否包含7。如果找到某个数字的因数中不包含7,那么就输出这个数字。

    has7的复杂度为 O ( l o g n ) O(log n) O(logn),fact7的复杂度为 O ( n ⋅ l o g n ) O(\sqrt{n}\cdot log n) O(n logn),询问次数T最大为 1 0 5 10^5 105,输入的x最大为 1 0 7 10^7 107。极端情况下,要对每个数字判断一下它的因数中是否有7,复杂度为 O ( n n ⋅ l o g n ) O(n\sqrt{n}\cdot log n) O(nn logn),在n达到 1 0 7 10^7 107时,总运算次数一定会超过 1 0 8 10^8 108,会超时。
    实际情况,用该方法写出代码提交,可以得70分。

    解法2:筛法(100分)

    该问题要做 1 0 5 10^5 105次的询问,如果预先确定了每个可能的数字的的因数中是否包含7,那么每次询问的复杂度为 O ( 1 ) O(1) O(1)
    类比判断多次询问的数字是不是质数的问题,可以通过筛法求质数表,而后询问的方法来减少复杂度。本题也可以采用类似的筛法。

    数组fac7,fac7[i]表示数字i的因数中是否包含7,数组所有元素初值都为false。
    显然fac7[1]为false,i从2开始循环到 1 0 7 10^7 107

    • 如果fac7[i]为true,则略过。
    • 如果fac7[i]为false,那么使用has7函数判断数字i中是否某一位是7。如果是,那么对所有满足 i ≤ 1 0 7 i\le 10^7 i107的i的倍数j,都把fac7[j]设为true。

    对于输入的x,如果x的因数包含7,即fac7[x]为真,那么输出-1。
    否则,要输出x的下一个因数包含7的数字。如果在这里枚举x后的每个数字看其因数是否包含7,则复杂度过高。
    这里可以设一个链式结构,设数组nextNot7,nextNot7[i]表示数字i的下一个因数不包含7的数字。该数组的值可以在做筛法的过程中生成,具体见代码注释。
    在生成该数组后,x的下一个因数不包含7的数字就是nextNot7[x]
    【注意】输入的x可以是 1 0 7 10^7 107,而 1 0 7 10^7 107是一个因数不包含7的数字,它的下一个因数不包含7的数字是 10000001 10000001 10000001,因此在做筛法时,最后一次循环时i应该是10000001,使得nextNot7[10000000]为10000001才可以。

    【题解代码】

    解法1:枚举(70分)
    #include 
    using namespace std;
    bool has7(int n)//判断数字n的某一位中是否有数字7 
    {
    	for(int a = n; a > 0; a /= 10)//n都是正整数,可以用for循环 
    		if(a%10 == 7)
    			return true;
    	return false;
    }
    bool fac7(int n)//判断数字n是否有因数中包含7 
    {
    	for(int i = 1; i*i <= n; ++i)
    		if(n%i == 0 && (has7(i) || has7(n/i)))
    			return true;
    	return false;	
    }
    int main()
    {
    	int t, x, r;
    	scanf("%d", &t);
    	while(t--)
    	{
    		scanf("%d", &x);
    		if(fac7(x))
    			printf("-1\n");
    		else
    		{
    			for(r = x+1; fac7(r); ++r);//如果r因数中有7,就看下一个数字。直到找到一个因数中没有7的数字。 
    			printf("%d\n", r);
    		}
    	}
    	return 0;
    }
    
    • 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
    解法2:筛法(100分)
    #include 
    using namespace std;
    #define N 10000000
    bool fac7[N+2];//fac7[i]:数字i的因数中某一位是否包含7 
    int nextNot7[N];//lastNot7[i]:数字i向后找第1个因数中不包含7的数字 
    bool has7(int n)//判断数字n的某一位中是否有数字7 
    {
    	for(int a = n; a > 0; a /= 10)//n都是正整数,可以用for循环 
    		if(a%10 == 7)
    			return true;
    	return false;
    }
    void screen()//普通筛法 
    {
    	int lastNot7 = 1;//上一个因数中不包含7的数字 
    	for(int i = 2; i <= N+1; ++i)
    	{
    		if(fac7[i] == false)//如果还没有判断i
    		{
    			if(has7(i))//如果i中某一位包含7 
    			{ 
    				for(int j = i; j <= N; j += i)//i的所有倍数的因数中都包含7 
    					fac7[j] = true;
    			}
    			else//确定i的因数不包含7 
    			{
    				nextNot7[lastNot7] = i;//lastNot7的下一个不包含7的数字是i 
    				lastNot7 = i;//lastNot7变为i 
    			}
    		}
    	}
    }
    int main()
    {
    	int t, x, i;
    	screen();//筛法得到fac7
    	scanf("%d", &t);
    	while(t--)
    	{
    		scanf("%d", &x);
    		if(fac7[x])
    			printf("-1\n");
    		else
    			printf("%d\n", nextNot7[x]);
    	}
    	return 0; 
    }
    
    • 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
  • 相关阅读:
    为什么要用PLL时钟芯片替换传统晶体和振荡器?
    [ubuntu]给WSL子系统ubuntu安一个桌面环境
    658. 找到 K 个最接近的元素
    cpp浅析STL-set
    第18讲:MySQL中常用的日期函数以及基本使用
    【AI视野·今日Robot 机器人论文速览 第八十四期】Thu, 7 Mar 2024
    Java-注解
    发现智能合约中的 bug 的 7 个方法
    15位、7位可控字符下的任意命令执行
    有哪些编辑图片加文字的软件?这些软件值得收藏
  • 原文地址:https://blog.csdn.net/lq1990717/article/details/127688172