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;
}