您的位置:首页 > 其它

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。

完整代码:

#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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: