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