NOIP2018初赛总结(提高组)(试题+答案+简要解析)

NOIP2018初赛总结(提高组)

更新完毕(纯手敲),如果有错误请在下面留言

单选题

T1.下列四个不同进制的数中,与其它三项数值上不相等的是

A.((269)_{16})

B.((617)_{10})

C.((1151)_8)

D.((1001101011)_2)

答案:D

实际上D比ABC要多2。这种题我一般先算2,8,16进制的,十进制难算,那三个很好互相转。

T2.下列属于解释执行的程序设计语言是

A.C

B.C++

C.Pascal

D.Python

答案:D

常识题。前三项都是编译型语言。

T3.中国计算机学会于?年创办全国青少年计算机程序设计竞赛。

A.1983

B.1984

C.1985

D.1986

答案:B

CCF颂歌题。凭感觉做题即可

upd:附上官方地址

背景:1984年邓小平指出:“计算机的普及要从娃娃做起。”中国计算机学会于1984年创办全国青少年计算机程序设计竞赛(简 称:NOI),当年参加竞赛的有8000多人。这一新的活动形式受到党和政府的关怀,得到社会各界的关注与支持。中央领导王震同志出席了首届竞赛发 奖大会,并对此项活动给予了充分肯定。从此每年一次NOI活动,吸引越来越多的青少年投身其中。十几年来,通过竞赛活动培养和发现了大批计算机爱好者,选 拔出了许多优秀的计算机后备人才。当年的许多选手已成为计算机硕士、博士,有的已经走上计算机科研岗位。

T4.设根节点深度为0,一棵深度为h的满k(k>1)叉树,即除最后一层无任何叶子节点外,每一层上的所有节点都有k个子节点的树,共有?个节点

A.(displaystylefrac{k^{h+1}-1}{k-1})

B.(k^{h-1})

C.(k^h)

D.(displaystylefrac{k^{h-1}}{k-1})

答案:A

可以打表可以记结论,甚至你还可以现场推结论:等比数列求和。第0层的节点为1,第1层为k,第二层为(k^2),直到第(h)层为(k^h),等比数列求和公式为(frac{1-k^{h+1}}{1-k})即可选A。

T5.设某算法的时间复杂度函数的递推方程是T(n)=T(n-1)+n(n为正整数)及T(0)=1,则该算法的时间复杂度为

A.O(log n)

B.O(n log n)

C.O(n)

D.O(n^2)

答案:D

NOIP2015初赛第?题

T6.表达式(a*d-b*c)的前缀形式是

A.(ad*bc*-)

B.(-*ad*bc)

C.(a*d-b*c)

D.(-**adbc)

答案:B

卧槽慌得一批差点看成后缀表达式。可以直接模拟得到结果。

T7.在一条长度为1的线段上随机取两个点,则以这两个点为端点的线段的期望长度是

A.1/2

B.1/3

C.2/3

D.3/5

答案:B

可以通过几何概型+体积什么什么的求。建立一个三维坐标系Oxyz,x轴代表点A位置,y轴代表点B位置,z轴代表线段长度期望,那么长度的期望就是两个四面体拼起来的图形,顶点为(0,0,0),(1,0,0),(0,1,0),(0,0,1)和(1,1,0),(1,0,0),(0,1,0),(1,1,1)。这个几何体的体积为1/3(锥体体积为底面积乘以高再除以3),由于底面积为1,所以高度平均为1/3,即长度期望为1/3。zcysky的骗分思路也可以参考一下。

T8.关于Catalan数Cn=(2n)!/(n+1)!/n!,下列说法错误的是

A.Cn表示有n+1个节点的不同形态的二叉树的个数。

B.Cn表示含n对括号的合法括号序列的个数。

C.Cn表示长度为n的入栈序列对应的合法的出栈序列个数。

D.Cn表示通过连接顶点而将n+2边的凸多边形分成三角形的方法个数。

答案:A

A选项应该是n而不是n+1。可以把n=1、n=2时候暴力带入。

T9.假设一台抽奖机中有红、蓝两色的球,任意时刻按下抽奖按钮,都会等概率获得红球或蓝球之一。有足够多的人每人都用这台抽奖机抽奖,假如他们的策略均为:抽中蓝球则继续抽球,抽中红球则停止。最后每个人都把自己获得的所有球放到一个大箱子里,最终大箱子里的红球与蓝球的比例接近于

A.1:2

B.2:1

C.1:3

D.1:1

答案:D

考虑如果某个人抽中蓝球,就让他的儿子取抽球,那么每个人抽中红球蓝球的概率相等,那么答案为D。

T10.为了统计一个非负整数的二进制中1的个数,代码如下:

int CountBit(int x)
{
    int ret = 0;
    while (x)
    {
        ret++;
        _________;
    }
    return ret;
}

则空格内要填入的语句是

A.x>>=1

B.x&=x-1

C.x|=x>>1

D.x<<=1

答案:B

假设二进制末尾是100000的形式,-1之后就是011111,与之后就是000000,最后一个1就没了。这个B选项其实就是lowbit操作。

不定项题

T1.NOIP初赛中,选手可以带入考场的有

A.笔

B.橡皮

C.手机(关机)

D.草稿纸

答案:AB

详见CCF官方规则。一般是不允许带手机和草稿纸,各地规定不太一样呢。

upd:附上官方规则的地址,本题考得是第12条。

选手进入考场时,只许携带笔、橡皮等非电子文具入场。禁止携带任何电子产品或机器设备入场,无存储功能的手表除外;手机(关机)、U盘或移动硬盘、键盘、鼠标、闹钟、计算器、书籍、草稿纸及背包等物品必须存放在考场外。如有违规带入的,一经发现,NOI各省特派员可直接取消违规选手的参赛资格。

T2.2-3树是一种特殊的树,它满足两个条件:

  1. 每个内部节点有两个或三个子节点;
  2. 所有的叶子节点到根的路径长度相同。

如果一棵2-3树有10个叶节点,那么它可能有?个非叶节点。

A.5

B.6

C.7

D.8

答案:CD

考虑每一层的节点数是1->2->5->10和1->2->4->10的形式。

T3.下列关于最短路算法的说法正确的有

A.当图中不存在负权回路但是存在负权边时,Dijkstra算法不一定能求出源点到所有点的最短路。

B.当图中不存在负权边时,调动多次Dijkstra算法能求出每对顶点间最短路径。

C.图中存在负权回路时,调用一次Dijkstra算法也一定能求出源点到所有点的最短路。

D.当图中不存在负权边时,调用一次Dijkstra算法不能用于每对顶点间的最短路计算。

答案:ABD

Dijkstra:单源最短路,不支持负权边。当然负权回路也是不行的

T4.下列说法中,是树的性质的有

A.无环

B.任意两个节点之间有且只有一条简单路径

C.有且只有一个简单环

D.边的数目恰好是顶点数目-1

答案:ABD

树的性质常识题。

T5.下列关于图灵奖的说法中,正确的有

A.图灵奖是由电气和电子工程师协会(IEEE)设立的。

B.目前获得该奖项的华人学者只有姚期智教授衽。

C.其名称取自计算机科学的先驱、英国科学家艾伦·麦席森·图灵。

D.它是计算机界最负盛名、最崇高的一个奖项,有“计算机界的诺贝尔奖”之称。

答案:BCD

图灵奖是美国的ACM协会创办的。

问题求解

T1.甲乙丙丁四人在考虑周末要不要外出郊游。

已知

  1. 如果周末下雨,并且乙不去,则甲一定不去;
  2. 如果乙去,则丁一定去;
  3. 如果丙去,则丁一定不去;
  4. 如果丁不去,而且甲不去,则丙一定不去。

如果周末丙去了,则甲(去了/没去),乙(去了/没去),丁(去了/没去),周末(下雨、没下雨)。

答案:去了,没去,没去,没下雨

根据3,丙去了,那么丁没去

根据2的逆否,丁没去,乙一定没去

根据4,丙去了,丁没去,那么甲一定去了//upd于12.13

根据1,乙没去,甲去了,周末不下雨

T2.方程(a*b=(a mathrm{or} b)*(a mathrm{and} b)),在a和b都取[0,31]中的整数时,共有?组解。(*表示乘法;or表示按位或运算;and表示按位与运算)

答案:454

介绍一个结论:(a mathrm{and} blemin(a,b)lemax(a,b)le a mathrm{or} b<2*max(a,b))

最后一个小于号可能不太理解,因为乘以一个2一定要进位,a or b一定是不会进位的。前面的不等号很好理解

那么对答案的贡献形式一定是(left{egin{aligned}a mathrm{and} b=min(a,b)\a mathrm{or} b=max(a,b)end{aligned} ight.)。那么最小值的二进制一定是最大值的二进制的子集。枚举所有二进制可能的位数,由于要删去(a=b)的重复情况,那么答案为(displaystyle2 imesleft(sum_{i=0}^5mathrm{C}_{5}^i imes2^i ight)-32=454)

阅读程序写结果

程序自己打的,大括号换了行否则太不顺眼了。。。

#include <cstdio>
int main()
{
    int x;
    scanf("%d", &x);
    int res = 0;
    for (int i = 0; i < x; ++i)
    {
        if (i * i % x == 1)
        {
            ++res;
        }
    }
    printf("%d", res);
    return 0;
}

输入:15

输出:4

打表找0到14中所有数平方对15取模判断是否为1即可,最后是1,4,11,14是答案。建议验算几遍,以免出错。

#include <cstdio>
int n, d[100];
bool v[100];
int main()
{
    scanf("%d", &n);
    for (int i = 0; i < n; ++i)
    {
        scanf("%d", d + i);
        v[i] = false;
    }
    int cnt = 0;
    for (int i = 0; i < n; ++i)
    {
        if (!v[i])
        {
            for (int j = i; !v[j]; j = d[j])
            {
                v[j] = true;
            }
            ++cnt;
        }
    }
    printf("%d
", cnt);
    return 0;
}

输入:10 7 1 4 3 2 5 9 8 0 6

输出:6

置换的阶的计数。我tm一开始看成11个元素的置换了,把开头的10也算进去了算成了3。这个动手模拟很简单的。

#include <iostream>
using namespace std;
string s;
long long magic(int l, int r)
{
    long long ans = 0;
    for (int i = l; i <= r; ++i)
    {
        ans = ans * 4 + s[i] - 'a' + 1;
    }
    return ans;
}
int main()
{
    cin >> s;
    int len = s.length();
    int ans = 0;
    for (int l1 = 0; l1 < len; ++l1)
    {
        for (int r1 = l1; r1 < len; ++r1)
        {
            bool bo = true;
            for (int l2 = 0; l2 < len; ++l2)
            {
                for (int r2 = l2; r2 < len; ++r2)
                {
                    if (magic(l1, r1) == magic(l2, r2) && (l1 != l2 || r1 != r2))
                    {
                        bo = false;
                    }
                }
            }
            if (bo)
            {
                ans += 1;
            }
        }
    }
    cout << ans << endl;
    return 0;
}

输入:abacaba

输出:16

寻找字符串中独一无二的子串的个数。magic函数其实是[l,r]区间内的一个BKDRHash。什么鬼名字。话说CCF的程序效率太低了吧

#include <cstdio>
using namespace std;
const int N = 110;
bool isUse[N];
int n, t;
int a[N], b[N];
bool isSmall()
{
    for (int i = 1; i <= n; ++i)
        if (a[i] != b[i]) return a[i] < b[i];
    return false;
}
bool getPermutation(int pos)
{
    if (pos > n)
    {
        return isSmall();
    }
    for (int i = 1; i <= n; ++i)
    {
        if (!isUse[i])
        {
            b[pos] = i; isUse[i] = true;
            if (getPermutation(pos + 1))
            {
                return true;
            }
            isUse[i] = false;
        }
    }
    return false;
}
void getNext()
{
    for (int i = 1; i <= n; ++i)
    {
        isUse[i] = false;
    }
    getPermutation(1);
    for (int i = 1; i <= n; ++i)
    {
        a[i] = b[i];
    }
}
int main()
{
    scanf("%d%d", &n, &t);
    for (int i = 1; i <= n; ++i)
    {
        scanf("%d", &a[i]);
    }
    for (int i = 1; i <= t; ++i)
    {
        getNext();
    }
    for (int i = 1; i <= n; ++i)
    {
        printf("%d", a[i]);
        if (i == n) putchar('
'); else putchar(' ');
    }
    return 0;
}

输入1:6 10 1 6 4 5 3 2

输出1:2 1 3 5 6 4

输入2:6 200 1 5 3 4 2 6

输出2:3 2 5 6 1 4

不难看出本题是求排列的下t个排列,CCF这个程序实现的效率太菜了吧,竟然从初始排列不断枚举,直到排列大于给定排列。

对于输入1,可以直接手推

对于输入2,正解应该是康托展开(了解一下),但是也可以一次跳阶乘次步。例如当后3位是单调递减的时候,可以直接跳3的阶乘次步,直接转移到下一个后3位是单调递减的情况。能节省中间模拟的时间,这种方法对不会康托展开的同学(比如我)适用

完善程序

T1.对于一个1到n的排列P(即1到n中每一个数在P中出现了恰好一次),令(q_i)为第i个位置之后第一个比(P_i)值更大的位置,如果不存在这样的位置,则(q_i=n+1)。举例来说,如果n=5且P为1 5 4 2 3,则q为2 6 6 5 6。

下列程序读入了排列P,使用双向链表求解了答案。试补全程序。(第二空2分,其余3分)

数据范围(1le nle10^5)

#include <iostream>
using namespace std;
const int N = 100010;
int n;
int L[N], R[N], a[N];
int main()
{
    cin >> n;
    for (int i = 1; i <= n; ++i)
    {
        int x;
        cin >> x;
        ____(1)____;//a[x] = i
    }
    for (int i = 1; i <= n; ++i)
    {
        R[i] = ____(2)____;//i + 1
        L[i] = i - 1;
    }
    for (int i = 1; i <= n; ++i)
    {
        L[____(3)____] = L[a[i]];//R[a[i]]
        R[L[a[i]]] = R[____(4)____];//a[i]
    }
    for (int i = 1; i <= n; ++i)
    {
        cout << ____(5)____ << " ";//R[i]
    }
    cout << endl;
    return 0;
}

1.a[i]表示大小为i的元素所在位置。

2.对称地写下一行的东西。链表的下一个元素。

3.对称地写下一行的东西。删除链表中本元素,把本元素右边元素的左指针跨过本元素指向左侧元素。

4.还是对称。把左边元素右指针指向右侧元素。

5.输出第i个数的后继元素即第一个比他大的元素,即R[i]。

T2.一只小猪要买N件物品(N不超过1000)。

它要买的所有商品在两家商店里都有卖。第i件物品在第一家商店的价格是a[i],在第二家商店的价格是b[i],两个价格都不小于0且不超过10000。如果在第一家商店买的物品的总额不少于50000,那么在第一家店买的物品都可以打95折(价格变为原来的0.95倍)。

求小猪买齐所有物品所需最小的总额。

输入:第一行一个数N。接下来N行,每行两个数。第i行的两个数分别代表a[i],b[i]。

输出:输出一行一个数,表示最少需要的总额,保留两位小数。

试补全程序。(第一空2分,其余3分)

#include <cstdio>
#include <algorithm>
using namespace std;

const int Inf = 1000000000;
const int threshold = 50000;
const int maxn = 1000;

int n, a[maxn], b[maxn];
bool put_a[maxn];
int total_a, total_b;
double ans;
int f[threshold];

int main()
{
	scanf("%d", &n);
    total_a = total_b = 0;
    for (int i = 0; i < n; ++i)
    {
        scanf("%d%d", a + i, b + i);
        if (a[i] <= b[i]) total_a += a[i];
        else total_b += b[i];
    }
    ans = total_a + total_b;
    total_a = total_b = 0;
    for (int i = 0; i < n; ++i)
    {
        if (____(1)____)//0.95 * a[i] <= b[i]
        {
            put_a[i] = true;
            total_a += a[i];
        }
        else
        {
            put_a[i] = false;
            total_b += b[i];
        }
    }
    if (____(2)____)//total_a >= threshold
    {
        printf("%.2f", total_a * 0.95 + total_b);
        return 0;
    }
    f[0] = 0;
    for (int i = 1; i < threshold; ++i)
        f[i] = Inf;
    int total_b_prefix = 0;
    for (int i = 0; i < n; ++i)
    {
        if (!put_a[i])
        {
            total_b_prefix += b[i];
            for (int j = threshold - 1; j >= 0; --j)
            {
                if (____(3)____ >= threshold && f[j] != Inf)//total_a + j + a[i]
                    ans = min(ans, (total_a + j + a[i]) * 0.95 + ____(4)____);//f[j] + total_b - total_b_prefix
                f[j] = min(f[j] + b[i], j >= a[i] ? ____(5)____ : Inf);//f[j - a[i]]
            }
        }
    }
    printf("%.2f", ans);
    return 0;
}

一个奇怪的DP。

1.如果是a[i]<=b[i],那么要上面的统计有啥用。这里的循环其实是在假定需要打折的情况下,先挑选已经钦定的a[i]的物品。

2.判断如果打折,直接购买便宜的a是否已经ok了。如果是就不需要再舍弃b购买一个较贵的a了,直接输出即可。

3.注意到下面是total_a + j + a[i]乘的0.95,那么这个一定是a的总价,那么它一定要大于等于50000。

4.观察下面的min,f[j]+b[i]说明f数组记录的是和b的价格有关的,而且它必须要出现,那么就放到这里。注意到total_b_prefix和total_b没有用过,说明他俩必须要用上,由于prefix是前缀,所以让total_b减去total_b_prefix即可。

5.由于j>=a[i],那么j-a[i]>=0,考虑我们的背包转移,可以大胆猜想这里填f[j-a[i]]。

本题实际上的状态是f[j]代表在前i个物品中,购买a店物品j元情况下购买b店物品的最小价值。total_a+j+a[i]代表买所有钦定要买的+额外需要买的+这次买的,f[j]+total_b-total_b_prefix是指i到n中所有选b,前面按照f[j]的状态取,b的价格。最后f[j]和取这次的b[i]与从j-a[i]转移过来去一个min,表示j这个状态取b还是取a。


总结:这次考试我拿了95分,总体来说题和去年水平差不多,这套题还是比较注重思维能力吧,当然不少了CCF颂歌题,“常识”题。希望大家参加NOIP2018复赛时候RP++吧。

原文地址:https://www.cnblogs.com/oier/p/9783787.html