14级寒假集训————数论基础
2015-01-16 16:10
309 查看
题目链接:点击打开链接
A.
题目大意:
对自然数进行重新排列,排列规则如下:首先排列从1到n的奇数(升序),然后排列从1到n的偶数(升序)。输出这样排列后的第k个数。
解题思路:
首先分析,当n为偶数时,num(奇) == num(偶);当n为奇数时,num(奇) + 1 == num(偶)
。
接下来我们以(n + 1) / 2为分界线,把前面的num(奇)和后面的num(偶)分开。如果k小于等于(n+1) / 2,那么一定在奇数范围内,分析规律得出第k个数为k * 2 – 1;如果k大于(n+1) / 2,那么一定在偶数范围内,分析规律得出第k个数为(k – (n + 1) / 2 ) * 2。
完整代码:
B.
题目大意:
一个山丘周围有逆时针标号为0到n-1的洞,兔子躲在某个洞中。狼从标号0开始,沿逆时针方向追赶兔子。规则如下:例如m=2,n=6,狼进过洞的标号依次为0、2、4、0。如果兔子隐藏在标号为1、3、5的洞内,就不会被狼吃掉,我们称这样的洞为safe holes。
对于每组m和n,如果存在safe holes,输出YES,否则输出NO。
解题思路:
首先一定会想到如果m和n都是偶数或者m和n之间存在不为1的倍数关系,那么一定存在safe holes使得狼无法到达;
再想一想,如果gcd( m , n) == 1,那么一定不会存在safe holes,狼会找遍所有的洞,最终吃掉兔子。
完整代码:
C.
题目大意:
哥德巴赫猜想说:“每个不小于4的偶数可以表示成两个素数之和。”让我们定义一个新的猜想:“每个不小于12的整数可以表示成两个合数的和。”
解题思路:
首先会想到从4到n遍历,如果i 和 n – i 都是合数,那么输出即可。
再想一想,可以用素数筛法进行打表,把100000内的素数标记为true,合数标记为false。从4到n遍历,如果pri[ i ] 和pri[ n – i ]都是合数的话,输出即可。
附上另外一种思路:如果n是偶数,那么输出4和n-4即可,因为4是最小的合数,n-4也必为偶数(n >12);如果n是奇数,那么输出9和n - 9即可,因为9是合数里最小的奇数,n - 9必为偶数。
完整代码:
朴素方法
打表筛法
另外一种解法
D.
题目大意:
100层的电梯,电梯显示器会显示两位数字(不满两位高位用0替代),代表当前层数。但是由于机器故障,显示器的某些灯没有亮。给你当前显示器的数字n,问它有多少种可能的层数。
解题思路:
可以把显示器的灯想象成火柴棍,对于当前数字,通过增添火柴棍,使其构成另外一个数字。设n的十位为a,个位为b。a通过增添火柴棍所能构成Pa个数字,b通过增添火柴棍所能构成Pb个火柴棍。那么Pa * Pb即为所求。
0可以构成0和8;
1可以构成1、7、3、4、8、9、0;
2可以构成2、8;
3可以构成3、9、8;
4可以构成4、9、8;
5可以构成5、6、8、9;
6可以构成6、8;
7可以构成7、3、9、0、8;
8只可以构成8;
9可以构成9、8。
完整代码:
E.
题目大意:
略
解题思路:
预处理出前1000个斐波那契数,编写高精度加法函数,最后实现O(1)时间的查找。论C++和J***A写高精度的代码量。
完整代码:
C++版
J***A版
F.
题目大意:
给一个长度为n的整数序列a[0],a[1],•••,a[n - 1],找出两个整数a[ i ]和a[ j ] (i < j),使得a[i] – a[j]尽量大。
解题思路:
首先会想到O(n^2)的暴力解法,很可惜这样做一定会超时,因为n达到10^5.
这道题O(n)时间内可解。对于每一个固定的j,我们应选择的是小于j且a[ i ]最大的i ,而和a[ j ]的具体值无关。我们从小到大枚举j , 顺便维护a[ i ]的最大值即可。
完整代码:
A.
题目大意:
对自然数进行重新排列,排列规则如下:首先排列从1到n的奇数(升序),然后排列从1到n的偶数(升序)。输出这样排列后的第k个数。
解题思路:
首先分析,当n为偶数时,num(奇) == num(偶);当n为奇数时,num(奇) + 1 == num(偶)
。
接下来我们以(n + 1) / 2为分界线,把前面的num(奇)和后面的num(偶)分开。如果k小于等于(n+1) / 2,那么一定在奇数范围内,分析规律得出第k个数为k * 2 – 1;如果k大于(n+1) / 2,那么一定在偶数范围内,分析规律得出第k个数为(k – (n + 1) / 2 ) * 2。
完整代码:
#include <iostream> #include <cstdio> using namespace std; __int64 n , k; void solve() { printf("%I64d\n" , k <= (n + 1) / 2 ? k * 2 - 1 : (k - (n + 1) / 2) * 2); } int main() { #ifdef DoubleQ freopen("in.txt","r",stdin); #endif while(~scanf("%I64d%I64d" , &n , &k)) solve(); return 0; }
B.
题目大意:
一个山丘周围有逆时针标号为0到n-1的洞,兔子躲在某个洞中。狼从标号0开始,沿逆时针方向追赶兔子。规则如下:例如m=2,n=6,狼进过洞的标号依次为0、2、4、0。如果兔子隐藏在标号为1、3、5的洞内,就不会被狼吃掉,我们称这样的洞为safe holes。
对于每组m和n,如果存在safe holes,输出YES,否则输出NO。
解题思路:
首先一定会想到如果m和n都是偶数或者m和n之间存在不为1的倍数关系,那么一定存在safe holes使得狼无法到达;
再想一想,如果gcd( m , n) == 1,那么一定不会存在safe holes,狼会找遍所有的洞,最终吃掉兔子。
完整代码:
#include <cstdio> #include <iostream> using namespace std; int gcd(int a , int b) { return b == 0 ? a : gcd(b , a % b); } void solve() { int n , m; scanf("%d%d",&n,&m); printf("%s\n" , gcd(n , m) != 1 ? "YES" : "NO"); } int main() { #ifdef DoubleQ freopen("in.txt","r",stdin); #endif int T; scanf("%d",&T); while(T--) solve(); return 0; }
C.
题目大意:
哥德巴赫猜想说:“每个不小于4的偶数可以表示成两个素数之和。”让我们定义一个新的猜想:“每个不小于12的整数可以表示成两个合数的和。”
解题思路:
首先会想到从4到n遍历,如果i 和 n – i 都是合数,那么输出即可。
再想一想,可以用素数筛法进行打表,把100000内的素数标记为true,合数标记为false。从4到n遍历,如果pri[ i ] 和pri[ n – i ]都是合数的话,输出即可。
附上另外一种思路:如果n是偶数,那么输出4和n-4即可,因为4是最小的合数,n-4也必为偶数(n >12);如果n是奇数,那么输出9和n - 9即可,因为9是合数里最小的奇数,n - 9必为偶数。
完整代码:
朴素方法
#include <iostream> #include <cstdio> #include <cmath> using namespace std; int n; bool is(int key) { int m = sqrt(key); for(int i = 2 ; i <= m ; i ++) if(key % i == 0) return true; return false; } void solve() { for(int i = 4 ; i <= n ; i ++) if(is(i) && is(n - i)) { printf("%d %d\n", i , n - i); return; } } int main() { #ifdef DoubleQ freopen("in.txt","r",stdin); #endif while(~scanf("%d",&n)) solve(); return 0; }
打表筛法
#include <iostream> #include <cstdio> #include <cmath> #include <cstring> using namespace std; int n; const int maxn = 1000001; bool pri[maxn]; void is() { memset(pri , true , sizeof(pri)); pri[0] = pri[1] = false; int m = sqrt(maxn); for(int i = 2 ; i <= m ; i ++) { if(pri[i]) { for(int j = i * i ; j < maxn ; j += i) pri[j] = false; } } } void solve() { for(int i = 4 ; i <= n ; i ++) if(!pri[i] && !pri[n - i]) { printf("%d %d\n", i , n - i); return; } } int main() { #ifdef DoubleQ freopen("in.txt","r",stdin); #endif is(); while(~scanf("%d",&n)) solve(); return 0; }
另外一种解法
#include <iostream> #include <cstdio> using namespace std; int n; int main() { while(~scanf("%d", &n)) { if(n%2==0) printf("4 %d\n", n - 4); else printf("9 %d\n", n - 9); } return 0; }
D.
题目大意:
100层的电梯,电梯显示器会显示两位数字(不满两位高位用0替代),代表当前层数。但是由于机器故障,显示器的某些灯没有亮。给你当前显示器的数字n,问它有多少种可能的层数。
解题思路:
可以把显示器的灯想象成火柴棍,对于当前数字,通过增添火柴棍,使其构成另外一个数字。设n的十位为a,个位为b。a通过增添火柴棍所能构成Pa个数字,b通过增添火柴棍所能构成Pb个火柴棍。那么Pa * Pb即为所求。
0可以构成0和8;
1可以构成1、7、3、4、8、9、0;
2可以构成2、8;
3可以构成3、9、8;
4可以构成4、9、8;
5可以构成5、6、8、9;
6可以构成6、8;
7可以构成7、3、9、0、8;
8只可以构成8;
9可以构成9、8。
完整代码:
#include <iostream> #include <cstdio> using namespace std; int n; int a[] = {2 , 7 , 2 , 3 , 3 , 4 , 2 , 5 , 1 , 2}; void solve() { printf("%d\n" , a[n % 10] * a[n / 10]); } int main() { #ifdef DoubleQ freopen("in.txt","r",stdin); #endif while(~scanf("%d",&n)) solve(); return 0; }
E.
题目大意:
略
解题思路:
预处理出前1000个斐波那契数,编写高精度加法函数,最后实现O(1)时间的查找。论C++和J***A写高精度的代码量。
完整代码:
C++版
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn = 1001; string f[maxn]; string add(string a , string b) { int lena = a.length(); int lenb = b.length(); int maxlen = max(lena , lenb); int minlen = min(lena , lenb); string maxx = lena >= lenb ? a : b; string minn = lena < lenb ? a : b; string result = ""; reverse(maxx.begin() , maxx.end()); reverse(minn.begin() , minn.end()); int k = 0; int key; for(int i = 0 ; i < minlen ; i ++) { key = (maxx[i] - '0') + (minn[i] - '0') + k; if(key > 9) { k = key / 10; key -= k * 10; } else k = 0; result += (char)(key + '0'); } for(int i = minlen ; i < maxlen ; i ++) { key = (maxx[i] - '0') + k; if(key > 9) { k = key / 10; key -= k * 10; } else k = 0; result += (char)(key + '0'); } if(k) result += (char)(k + '0'); reverse(result.begin() , result.end()); return result; } void fib() { f[1] = f[2] = "1"; for(int i = 3 ; i < maxn ; i ++) f[i] = add(f[i-1] , f[i-2]); } void solve() { int n; scanf("%d",&n); cout << f << endl; } int main() { #ifdef DoubleQ freopen("in.txt" , "r" , stdin); #endif; int T; scanf("%d",&T); fib(); while(T--) solve(); return 0; }
J***A版
import java.math.*; import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner cin = new Scanner(System.in); BigInteger m[] = new BigInteger[1001]; BigInteger start = new BigInteger("1"); m[1] = start; m[2] = start; for(int i = 3 ; i < 1001 ; i ++){ m[i] = m[i-1].add(m[i-2]); } int n = cin.nextInt(); while(n != 0){ int key = cin.nextInt(); System.out.println(m[key]); n--; } } }
F.
题目大意:
给一个长度为n的整数序列a[0],a[1],•••,a[n - 1],找出两个整数a[ i ]和a[ j ] (i < j),使得a[i] – a[j]尽量大。
解题思路:
首先会想到O(n^2)的暴力解法,很可惜这样做一定会超时,因为n达到10^5.
这道题O(n)时间内可解。对于每一个固定的j,我们应选择的是小于j且a[ i ]最大的i ,而和a[ j ]的具体值无关。我们从小到大枚举j , 顺便维护a[ i ]的最大值即可。
完整代码:
#include <cstdio> #include <iostream> #include <algorithm> using namespace std; const int maxn = 1000001; int a[maxn]; void solve() { int n; scanf("%d",&n); for(int i = 0 ; i < n ; i ++) scanf("%d",&a[i]); int maxi = a[0]; int maxx = a[0] - a[1]; for(int i = 1 ; i < n ; i ++) { maxi - a[i] > maxx ? maxx = maxi - a[i] : maxx = maxx; a[i] > maxi ? maxi = a[i] : maxx = maxx; } printf("%d\n",maxx); } int main() { #ifdef DoubleQ freopen("in.txt","r",stdin); #endif int T; scanf("%d",&T); while(T--) solve(); return 0; }
相关文章推荐
- 2015年14级寒假集训--素数筛
- 寒假训练报告2.2(数论基础)
- SDUT14级寒假集训-找女朋友- 快排
- [2016/8/2][暑假集训]数论基础
- HLJU14级寒假集训之最短路专场
- 数论基础_欧拉函数
- CDU集训代码:初等数论
- 寒假集训小记
- 数论基础
- 寒假集训附加题目题解报告(4)——3n+1数链长度问题
- 数论基础_线性同余方程
- 寒假集训附加题目题解报告(1)——数列
- CDU集训代码:基础算法和数据结构
- 《数论》3.6习题3------求一元线性同余方程所有解(不是方程组)基础练习例子&&求逆元
- 数论基础(维诺格拉多夫著,裘光明译) 勘误
- 数论基础
- 数论基础_中国剩余定理
- 数论基础(维诺格拉多夫著,裘光明译) 勘误
- 数论基础_欧几里德算法
- [Asia - Hefei - 2008/2009][B:Discrete Square Roots][数论基础]