1998 CSP-J/NOIP复赛真题题解——秒杀T1.三连击,T2.求阶乘,T3.幂次方

[NOIP1998 普及组] 三连击

题干

题目背景

本题为提交答案题,您可以写程序或手算在本机上算出答案后,直接提交答案文本,也可提交答案生成程序。

题目描述

1 , 2 , … , 9 1, 2, \ldots , 9 1,2,,9 9 9 9 个数分成 3 3 3 组,分别组成 3 3 3 个三位数,且使这 3 3 3 个三位数构成 1 : 2 : 3 1 : 2 : 3 1:2:3 的比例,试求出所有满足条件的 3 3 3 个三位数。

输入格式

输出格式

若干行,每行 3 3 3 个数字。按照每行第 1 1 1 个数字升序排列。

样例 #1

样例输入 #1
样例输出 #1
192 384 576
* * *
...

* * *
(剩余部分不予展示)

提示

NOIP1998 普及组 第一题

解析

一眼看过去,暴力枚举,秒了!三个数比例为 1 : 2 : 3 1:2:3 123,而三位数中最多有 ( 1000 ➗ 3 ) = 333 (1000➗3)=333 1000➗3=333个这样的数(包含了一位,两位,三位,但也无伤大雅),又因为要使用 1 − 9 1-9 19这几个数字,每个数字都不能重复使用,且必须使用,则可以编写一个函数,使用标记数组标记这三个数是否由数字 1 − 9 1-9 19中的所有数字组成的(一、二位数中可以看作是百位为零)。

时间复杂度

逆天 O ( 1 )!!!!全都是常数项!!!! 逆天O(1)!!!!全都是常数项!!!! 逆天O1)!!!!全都是常数项!!!!

空间复杂度

还是逆天的 O ( 1 )!!!!又都是常数项 还是逆天的O(1)!!!!又都是常数项 还是逆天的O1)!!!!又都是常数项

实现代码

#include <bits/stdc++.h>
using namespace std ;
int a[10] ;
//标记数组,用于标记某个数字是否出现
void func(int k){
	while(k)a[k % 10] = 1,k /= 10 ;
	return ;
}
//数字拆分,标记出现的数字
bool pd(){
	for(int i = 1;i < 10;++ i)
		if(!a[i])return false ;
	return true ;
}
//判断是否1~9都用上了
int main(){
	freopen("three.in","r",stdin) ;
	freopen("three.out","w",stdout) ;
	for(int i = 100;i < 334;++ i){
        //暴力枚举,从最小的三位数100到(1000除以3)=333
		for(int o = 0;o < 10;++ o)
			a[o] = 0 ;
        //清空标记数组
		func(i) ;
        //标记第一个数
		func(i << 1) ;
        //标记第二个数
		func(i * 3) ;
        //标记第三个数
		if(pd())printf("%d %d %d\n",i,i << 1,i * 3) ;
        //输出答案
	}
	return 0 ;
}

[NOIP1998 普及组] 阶乘之和

题干

题目描述

用高精度计算出 S = 1 ! + 2 ! + 3 ! + ⋯ + n ! S = 1! + 2! + 3! + \cdots + n! S=1!+2!+3!++n! n ≤ 50 n \le 50 n50)。

其中 ! 表示阶乘,定义为 n ! = n × ( n − 1 ) × ( n − 2 ) × ⋯ × 1 n!=n\times (n-1)\times (n-2)\times \cdots \times 1 n!=n×(n1)×(n2)××1。例如, 5 ! = 5 × 4 × 3 × 2 × 1 = 120 5! = 5 \times 4 \times 3 \times 2 \times 1=120 5!=5×4×3×2×1=120

输入格式

一个正整数 n n n

输出格式

一个正整数 S S S,表示计算结果。

样例 #1

样例输入 #1
3
样例输出 #1
9

提示

【数据范围】

对于 100 % 100 \% 100% 的数据, 1 ≤ n ≤ 50 1 \le n \le 50 1n50

【其他说明】

注,《深入浅出基础篇》中使用本题作为例题,但是其数据范围只有 n ≤ 20 n \le 20 n20,使用书中的代码无法通过本题。

如果希望通过本题,请继续学习第八章高精度的知识。

NOIP1998 普及组 第二题

解析

这一题一眼模板题呀!大数算法秒了呀!我们可以使用 v e c t o r < i n t > vector<int> vector<int>来存储一个数字的每一位,把它当作一个数来处理,就像我们小学列竖式那样,每两位两两相乘,再把所有相乘的结果相加,就得到了答案。话不多说,上代码!!!

时间复杂度

O ( n 2 ),双重循环,秒了! O(n²),双重循环,秒了! On2),双重循环,秒了!

空间复杂度

O ( n ),主要需要内存去存储那个大数,秒了! O(n),主要需要内存去存储那个大数,秒了! On),主要需要内存去存储那个大数,秒了!

实现代码

#include <bits/stdc++.h>
using namespace std ;
vector<int> s(1,1),now(1,1) ;
int n,temp,zero ;
int main(){
	freopen("factor.in","r",stdin) ;
	freopen("factor.out","w",stdout) ;
	scanf("%d",&n) ;
	for(int i = 2;i <= n;++ i){
		temp = 0 ;
		for(int o = 0;o < now.size();++ o){
			now[o] = (temp += (now[o] * i)) % 10 ;
			temp /= 10 ; 
		}
		while(temp)now.push_back(temp % 10),temp /= 10 ;
		//相乘操作,当前的大数类乘以i 
		s.resize(max(s.size(),now.size()) + 1) ;
		now.resize(s.size()) ;
		for(int o = 0;o < s.size();++ o){
			s[o + 1] += ((s[o] += now[o]) / 10) ;
			s[o] %= 10 ;
		}
		//相加操作,累计答案 
		zero = s.size() - 1 ;
		while(zero >= 0 && s[zero] == 0)-- zero ;
		s.resize(zero + 1) ;
		//去除前缀零 
		zero = now.size() - 1 ;
		while(zero >= 0 && now[zero] == 0)-- zero ;
		now.resize(zero + 1) ;
		//去除前缀零 
	}
	for(int i = s.size() - 1;i >= 0;-- i)
		putchar('0' + s[i]) ;
	//因为逆序存储比较方便,进位的时候只要在最后面增加即可
	//所以输出要逆回来,逆逆序输出,也就是顺序输出 
	return 0 ;
}

[NOIP1998 普及组] 幂次方

题干

题目描述

任何一个正整数都可以用 2 2 2 的幂次方表示。例如 $137=27+23+2^0 $。

同时约定次方用括号来表示,即 a b a^b ab 可表示为 a ( b ) a(b) a(b)

由此可知, 137 137 137 可表示为 2 ( 7 ) + 2 ( 3 ) + 2 ( 0 ) 2(7)+2(3)+2(0) 2(7)+2(3)+2(0)

进一步:

7 = 2 2 + 2 + 2 0 7= 2^2+2+2^0 7=22+2+20 ( 2 1 2^1 21 2 2 2 表示),并且 3 = 2 + 2 0 3=2+2^0 3=2+20

所以最后 137 137 137 可表示为 2 ( 2 ( 2 ) + 2 + 2 ( 0 ) ) + 2 ( 2 + 2 ( 0 ) ) + 2 ( 0 ) 2(2(2)+2+2(0))+2(2+2(0))+2(0) 2(2(2)+2+2(0))+2(2+2(0))+2(0)

又如 1315 = 2 10 + 2 8 + 2 5 + 2 + 1 1315=2^{10} +2^8 +2^5 +2+1 1315=210+28+25+2+1

所以 1315 1315 1315 最后可表示为 2 ( 2 ( 2 + 2 ( 0 ) ) + 2 ) + 2 ( 2 ( 2 + 2 ( 0 ) ) ) + 2 ( 2 ( 2 ) + 2 ( 0 ) ) + 2 + 2 ( 0 ) 2(2(2+2(0))+2)+2(2(2+2(0)))+2(2(2)+2(0))+2+2(0) 2(2(2+2(0))+2)+2(2(2+2(0)))+2(2(2)+2(0))+2+2(0)

输入格式

一行一个正整数 n n n

输出格式

符合约定的 n n n 0 , 2 0, 2 0,2 表示(在表示中不能有空格)。

样例 #1

样例输入 #1
1315
样例输出 #1
2(2(2+2(0))+2)+2(2(2+2(0)))+2(2(2)+2(0))+2+2(0)

提示

【数据范围】

对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 2 × 10 4 1 \le n \le 2 \times {10}^4 1n2×104

NOIP1998 普及组 第三题

解析

突然发现,这不就是简单递归吗!秒了呀!编写一个递归函数叫做f,每次检测有哪些二进制位为1,则输出2(f(最高为一二进制位的位置))+2(f(第二高为一二进制位的位置))+…+2(f(最低为一二进制位的位置)),秒了呀!!!!!

时间复杂度

O ( l o g n ) O(logn) Ologn

空间复杂度

O ( l o g n ) O(logn) O(logn)

实现代码

#include <stdio.h>
void f(int n){
	//特判为1的情况 
	if(n == 1)printf("2(0)") ;
	//特判为2的情况 
	else if(n == 2)printf("2") ;
	else{
		int now = 1,i = 0 ;
		//寻找最高位 
		do{
			now <<= 1 ;
			if(now > n){
				//找到最高位 
				now >>= 1 ;
				if(i == 1)printf("2") ;
				//特判为1的情况 
				else{
					//递归输出 
					printf("2(") ;
					f(i) ;
					putchar(')') ;
				}
				if(n > now){
					//输出剩余部分 
					putchar('+') ;
					f(n - now) ;
				}
				return ;
			}else ++ i ;
		}while(1) ;
	}
	return ;
} 
int main(){
	freopen("power.in","r",stdin) ;
	freopen("power.out","w",stdout) ;
    int n ;
    scanf("%d",&n) ;
    f(n) ;
    return 0 ;
} 

尾声

又成功地“秒杀”了一年的CSP/NOIP,祝读者水平高升,参加竞赛都能“秒杀”取得好成绩!

  • 21
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值