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

T1.三连击

题干

题目背景

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

题目描述

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

输入格式

输出格式

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

输入输出样例

输入 #1复制
输出 #1复制
192 384 576
* * *
...

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

说明/提示

NOIP1998 普及组 第一题

解析

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

时间复杂度

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

空间复杂度

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

实现代码

#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 ;
}

T2.求阶乘(factor)

题干

题目描述

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

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

输入格式

一个正整数 n。

输出格式

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

输入输出样例

输入 #1复制

3

输出 #1复制

9

说明/提示

【数据范围】

对于 100% 的数据,1≤n≤50。

【其他说明】

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

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

NOIP1998 普及组 第二题

解析

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

时间复杂度

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

空间复杂度

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

实现代码

#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 ;
}

T3.求幂次方(power)

题干

题目描述

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

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

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

进一步:

7=2^2+2+2^0 ( 2^1 用 2 表示),并且 3=2+2^0。

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

又如 1315=210+28+25+2+1

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

输入格式

一行一个正整数 n。

输出格式

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

输入输出样例

输入 #1复制

1315

输出 #1复制

2(2(2+2(0))+2)+2(2(2+2(0)))+2(2(2)+2(0))+2+2(0)

说明/提示

【数据范围】

对于 100% 的数据,1≤n≤2×104。

NOIP1998 普及组 第三题

解析

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

时间复杂度

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,祝读者水平高升,参加竞赛都能“秒杀”取得好成绩!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值