[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 1:2:3,而三位数中最多有 ( 1000 ➗ 3 ) = 333 (1000➗3)=333 (1000➗3)=333个这样的数(包含了一位,两位,三位,但也无伤大雅),又因为要使用 1 − 9 1-9 1−9这几个数字,每个数字都不能重复使用,且必须使用,则可以编写一个函数,使用标记数组标记这三个数是否由数字 1 − 9 1-9 1−9中的所有数字组成的(一、二位数中可以看作是百位为零)。
时间复杂度
逆天 O ( 1 )!!!!全都是常数项!!!! 逆天O(1)!!!!全都是常数项!!!! 逆天O(1)!!!!全都是常数项!!!!
空间复杂度
还是逆天的 O ( 1 )!!!!又都是常数项 还是逆天的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 ;
}
[NOIP1998 普及组] 阶乘之和
题干
题目描述
用高精度计算出 S = 1 ! + 2 ! + 3 ! + ⋯ + n ! S = 1! + 2! + 3! + \cdots + n! S=1!+2!+3!+⋯+n!( n ≤ 50 n \le 50 n≤50)。
其中 !
表示阶乘,定义为
n
!
=
n
×
(
n
−
1
)
×
(
n
−
2
)
×
⋯
×
1
n!=n\times (n-1)\times (n-2)\times \cdots \times 1
n!=n×(n−1)×(n−2)×⋯×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 1≤n≤50。
【其他说明】
注,《深入浅出基础篇》中使用本题作为例题,但是其数据范围只有 n ≤ 20 n \le 20 n≤20,使用书中的代码无法通过本题。
如果希望通过本题,请继续学习第八章高精度的知识。
NOIP1998 普及组 第二题
解析
这一题一眼模板题呀!大数算法秒了呀!我们可以使用 v e c t o r < i n t > vector<int> vector<int>来存储一个数字的每一位,把它当作一个数来处理,就像我们小学列竖式那样,每两位两两相乘,再把所有相乘的结果相加,就得到了答案。话不多说,上代码!!!
时间复杂度
O ( n 2 ),双重循环,秒了! O(n²),双重循环,秒了! O(n2),双重循环,秒了!
空间复杂度
O ( n ),主要需要内存去存储那个大数,秒了! 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 ;
}
[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 1≤n≤2×104。
NOIP1998 普及组 第三题
解析
突然发现,这不就是简单递归吗!秒了呀!编写一个递归函数叫做f,每次检测有哪些二进制位为1,则输出2(f(最高为一二进制位的位置))+2(f(第二高为一二进制位的位置))+…+2(f(最低为一二进制位的位置)),秒了呀!!!!!
时间复杂度
O ( l o g n ) O(logn) O(logn)
空间复杂度
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,祝读者水平高升,参加竞赛都能“秒杀”取得好成绩!