2013年NOIP普及组复赛真题解析

2013年NOIP普及组T1-计数问题

题目描述

试计算在区间 1 到 n 的所有整数中,数字 x(0 ≤ x ≤ 9)共出现了多少次?例如,在 1到 11 中,即在 1、2、3、4、5、6、7、8、9、10、11 中,数字 1 出现了 4 次。

输入格式

输入文件名为 count.in。

输入共 1 行,包含 2 个整数 n、x,之间用一个空格隔开。

输出格式

输出文件名为 count.out。

输出共 1 行,包含一个整数,表示 x 出现的次数。

输入输出样例

输入样例1:
11 1
输出样例1:
4

说明

【数据范围】

对于 100%的数据,1≤ n ≤ 1,000,000,0 ≤ x ≤ 9。

耗时限制1000ms   内存限制128MB

解析:

考点:循环结构,数位分离

参考代码:


#include<bits/stdc++.h>
using namespace std;
int main(){
    int i,n,m,s=0,a,b;
    cin>>n>>m;
    for(i=1;i<=n;i++){//枚举1到n各个数中那些数字里有m
        a=i;//得给a赋值因为a在下面会被除为0
        while(a!=0) {//数位分离法
            b=a%10;
            if(b==m){//找出跟m相同的数
                s++;//累加
            }
            a=a/10;
        }
    }
    cout<<s;//输出
}

2013年NOIP普及组T2-表达式求值

题目描述

给定一个只包含加法和乘法的算术表达式,请你编程计算表达式的值。

输入格式

输入仅有一行,为需要你计算的表达式,表达式中只包含数字、加法运算符“+”和乘法运算符“*”,且没有括号,所有参与运算的数字均为 0 到 2^31-1 之间的整数。

输入数据保证这一行只有 0~ 9、+、*这 12 种字符。

输出格式

输出只有一行,包含一个整数,表示这个表达式的值。注意:当答案长度多于 4 位时,

请只输出最后 4 位,前导 0 不输出。

输入输出样例

输入样例1:
1+1*3+4
输出样例1:
8
输入样例2:
1+1234567890*1
输出样例2:
7891
输入样例3:
1+1000000003*1
输出样例3:
4

说明

【数据规模】

对于 30%的数据,0≤表达式中加法运算符和乘法运算符的总数≤100;

对于 80%的数据,0≤表达式中加法运算符和乘法运算符的总数≤1000;

对于 100%的数据,0≤表达式中加法运算符和乘法运算符的总数≤100000。

耗时限制1000ms  内存限制128MB

解析:

考点:模拟,栈

参考代码:

1.手写栈

#include <bits/stdc++.h>
using namespace std;
int s[100010]; // 数字栈
int p = 0; // 栈顶下标
int main() {
    int n, ans = 0;
    char c;
    cin >> n;
    s[++p] = n; //第一个输入数字入栈
    while (cin >> c >> n) {
        if (c == '*')  //是乘法,就取出栈顶乘n之后放入栈顶
            s[p] = n * s[p] % 10000;
        else //是加法,直接入栈即可
            s[++p] = n;
    }
    while (p) { // 栈不为空,求一次和,出一次栈
        ans += s[p--];
        ans %= 10000;
    }
    cout << ans;
}

2.stl栈

#include <bits/stdc++.h>
using namespace std;
stack<int> s;

int main() {
    int n, ans = 0;
    char c;
    cin >> n;
    s.push(n);  //第一个输入数字入栈
    while (cin >> c >> n) {
        if (c == '*') {
            int t = s.top();          //获取栈顶元素
            s.pop();                  //出栈
            s.push(t * n % 10000);    //将乘法的结果入栈
        } else  //是加法,直接入栈即可
            s.push(n);
    }
    while (!s.empty()) { // 栈不为空,求一次和,出一次栈
        ans += s.top();
        s.pop();
        ans %= 10000;
    }
    cout << ans;
}

2013年NOIP普及组T3-小朋友的数字

题目描述

有 n 个小朋友排成一列。每个小朋友手上都有一个数字,这个数字可正可负。规定每个小朋友的特征值等于排在他前面(包括他本人)的小朋友中连续若干个(最少有一个)小朋友手上的数字之和的最大值。

作为这些小朋友的老师,你需要给每个小朋友一个分数,分数是这样规定的:第一个小朋友的分数是他的特征值,其它小朋友的分数为排在他前面的所有小朋友中(不包括他本人),小朋友分数加上其特征值的最大值。

请计算所有小朋友分数的最大值,输出时保持最大值的符号,将其绝对值对 p 取模后输出。

输入格式

输入文件为 number.in。

第一行包含两个正整数 n、p,之间用一个空格隔开。

第二行包含 n 个数,每两个整数之间用一个空格隔开,表示每个小朋友手上的数字。

输出格式

输出文件名为 number.out。

输出只有一行,包含一个整数,表示最大分数对 p 取模的结果。

输入输出样例

输入样例1:
5 997
1 2 3 4 5 
输出样例1:
21
输入样例2:
5 7 
-1 -1 -1 -1 -1 
输出样例2:
-1

说明

【样例1说明】

小朋友的特征值分别为 1、3、6、10、15,分数分别为 1、2、5、11、21,最大值 21

对 997 的模是 21。

【样例2说明】

小朋友的特征值分别为-1、-1、-1、-1、-1,分数分别为-1、-2、-2、-2、-2,最大值

-1 对 7 的模为-1,输出-1。

【数据范围】

对于 50%的数据,1 ≤ n ≤ 1,000,1 ≤ p ≤ 1,000所有数字的绝对值不超过 1000;

对于 100%的数据,1 ≤ n ≤ 1,000,000,1 ≤ p ≤ 10^9,其他数字的绝对值均不超过 10^9。

耗时限制1000ms   内存限制128MB

解析:

考点:动态规划,线性DP

思路:线性DP(最大连续子序列)

我们可以先用 最大连续子序列的思路 求出每个人的特征值,再按照题目求出每个人的分数,之后我们再找到最大的分数,就是答案了。

注意:题目描述的取余操作有点坑,注意理解。

#include<bits/stdc++.h>
using namespace std;

#define ll long long

const int N = 1e6 + 10;

ll n, p, s[N], t[N], f[N], maxn = INT_MIN, x, ans;

int main(){
    ios::sync_with_stdio(0); //数据量级有100万,加快读入
    cin.tie(0);
    
    cin >> n >> p;
    for (ll i = 1; i <= n; i ++){
        cin >> x;
        s[i] = max(s[i - 1] + x, x);//最大连续子序列
        maxn = max(s[i], maxn);//计算特征值
        t[i] = maxn % p;//记录第i位同学的特征值
    }
    
    ans = f[1] = t[1];//初始化
    maxn = INT_MIN;//初始化
    
    for (ll i = 2; i <= n; i ++){
        maxn = max(maxn, t[i - 1] + f[i - 1]);//求出每个人的分数
        f[i] = maxn;
        if (ans < maxn) ans = maxn % p; //求出最大值
    }
    
    cout << ans;//输出
    return 0;
}

2013年NOIP普及组T4-车站分级

题目描述

一条单向的铁路线上,依次有编号为 1, 2, …, n 的 n 个火车站。每个火车站都有一个级别,最低为 1 级。现有若干趟车次在这条线路上行驶,每一趟都满足如下要求:如果这趟车次停靠了火车站 x,则始发站、终点站之间所有级别大于等于火车站 x 的都必须停靠。(注意:起始站和终点站自然也算作事先已知需要停靠的站点)

例如,下表是 5 趟车次的运行情况。其中,前 4 趟车次均满足要求,而第 5 趟车次由于停靠了 3 号火车站(2 级)却未停靠途经的 6 号火车站(亦为 2 级)而不满足要求。

现有 m 趟车次的运行情况(全部满足要求),试推算这 n 个火车站至少分为几个不同的级别。

输入格式

输入文件为 level.in。

第一行包含 2 个正整数 n, m,用一个空格隔开。

第 i + 1 行(1 ≤ i ≤ m)中,首先是一个正整数 si(2 ≤ si ≤ n),表示第 i 趟车次有 si 个停靠站;接下来有 si个正整数,表示所有停靠站的编号,从小到大排列。每两个数之间用一个空格隔开。输入保证所有的车次都满足要求。

输出格式

输出文件为 level.out。

输出只有一行,包含一个正整数,即 n 个火车站最少划分的级别数。

输入输出样例

输入样例1:
9 2
4 1 3 5 6
3 3 5 6 
输出样例1:
2
输入样例2:
9 3 
4 1 3 5 6 
3 3 5 6 
3 1 5 9 
输出样例2:
3

说明

【数据范围】

对于 20%的数据,1 ≤ n, m ≤ 10;

对于 50%的数据,1 ≤ n, m ≤ 100;

对于 100%的数据,1 ≤ n, m ≤ 1000。

耗时限制1000ms   内存限制128MB

解析:

考点:图结构,拓扑排序,差分约束

差分约束裸题。

计算当前线路中最小的级别(比较始发站和终点站)。

整条线路中所有大于这个级别的都必须停靠

所有未停靠的站点的级别一定小于这个级别

也就是说所有未停靠的即为级别低,记为 A

所有停靠的站点级别一定比A的高,记作 B

得到公式B≥A+1

根据很明显是一道差分约束问题。

根据差分约束的概念,我们从所有的 A 向所有的 B 连一条权值为 1 的有向边。

然后根据差分约束的套路,我们还要设一个界限才能求出最大值。

因为所有车站级别都是正值,所以 A≥1,也就是从 0 向所有的 A 中的点连一条权值为 1 的有向边。我们常常用直接给dist数组赋值为 1 代替

但是由于实际数据范围较大

最坏情况下是有1000趟火车,每趟有1000个点,每趟上限有500个点停站,则有(1000 - 500)个点不停站,不停站的点都向停站的点连有向边,则总共有 500∗500∗1000=2.5∗10^8,差分约束的spfa有可能超时

由于本题中的所有点的权值都是大于 0,并且一定满足要求=>所有车站都等级森严=>不存在环=>可以拓扑排序得到拓扑图使用递推求解差分约束问题。

因此整体的思路为

  • 拓扑排序得拓扑图
  • “至少”=>=>要求的是最小值=>=>所有条件的下界中取最大值=>=>最长路,因此我们,根据拓扑序跑最长路递推即可。
  • 答案为满足所有约束条件的解中最大值既是题目要求的最高的级别

一个建图的优化

如果直接暴力建图就会建O(nm)条边,也就是2∗108个点,时间和空间都有可能超时。

我们可以在中间建一个虚拟节点,左边向虚拟点连 0,虚拟点向右边连 1 等价于左边向右边连 1

这样只会建 O(n+m) 条边

注意的是本题一共m 条线路,每条线路一个虚拟源点,所以一共会有 n+m 个点。

当年出题人并没有卡这一点,不然要卡死一大批有志青年。别忘了一共有n + m 个点。

参考代码

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>

using namespace std;

const int N = 2007, M = 5000007;

int n, m;
int ver[M], nex[M], edge[M], head[N], tot;
int din[N];
int vis[N];
int q[N];
int dist[N];

void add(int x, int y, int z){
    ver[tot] = y;
    edge[tot] = z;
    nex[tot] = head[x];
    head[x] = tot ++ ;
    din[y] ++ ;
}

void toposort(){
    int hh = 0, tt = -1;
    for(int i = 1;i <= n + m;++i){
        if(!din[i]){
            q[++ tt] = i;
            if(tt == N)tt = 0;
        }
    }

    while(hh <= tt){

        int x = q[hh ++ ];
        if(hh == N)hh = 0;

        for(int i = head[x];~i;i = nex[i]){
            int y = ver[i];
            if(-- din[y] == 0){
                q[++ tt] = y;
                if(tt == N)tt = 0;
            }
        }
    }
}

int main(){
    scanf("%d%d", &n,&m);
    memset(head,-1,sizeof head);

    for(int i = 1;i <= m;++i){
        memset(vis,0,sizeof vis);
        int t,stop;
        scanf("%d",&t);
        int start = n, end = 1;
        while(t -- ){
            scanf("%d",&stop);
            start = min(start, stop);
            end = max(end, stop);
            vis[stop] = true;
        }
        int source = n + i;
        for(int j = start;j <= end;++j){
            if(vis[j])
                add(source, j, 1);
            else add(j, source, 0);
        }
    }

    toposort();

    for(int i = 1;i <= n;++i)
        dist[i] = 1;
    for(int i = 0;i < n + m;++i){
        int x = q[i];
        for(int j = head[x];~j;j = nex[j]){
            int y = ver[j], z = edge[j]; 
            dist[y] = max(dist[y], dist[x] + z);
        }
    }
    int res = 0;
    for(int i = 1;i <= n;++i)
        res = max(res, dist[i]);

    printf("%d\n",res);
    return 0;
}

  • 22
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
2004年NOIP普及复赛数据是指在2004年举办的NOIP普及复赛中所使用的目和数据。NOIP是指全国青少年信息学奥林匹克竞赛,旨在培养和选拔青少年的计算机编程能力。 2004年复赛中的数据包括了一系列的目和测试用例。这些目主要考察了参赛选手的编程基础和解能力。每个目都有一些特定的要求和限制条件。通过这些目,考察选手对编程语言的掌握程度、算法的设计和实现能力、问分析与解决的能力等。 复赛的数据往往是由工作人员根据目要求设计和生成的。每个目可能包含了多个测试用例,用于验证程序的正确性和性能。参赛选手需要编写程序解决这些目,并保证程序能够正确地处理不同的测试用例。 在比赛中,选手需要根据目描述和输入数据,编写相应的程序来求解问。比赛规定了程序的运行时间和内存限制,选手需要在规定的时间和空间限制下编写高效的程序,并得到正确的输出结果。 通过参与2004年NOIP普及复赛,选手们在实际的编程竞赛中得到了锻炼和提高。他们学会了如何拆解问、设计算法、编写代码,并在紧张的比赛环境下克服困难,迅速解决问。 综上所述,2004年NOIP普及复赛数据是用于考察选手编程基础和解能力的一系列目和测试用例,选手需要根据目描述和输入数据编写程序,以期在比赛中表现出色。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值