概述

NOIP2016 普及组的前三题都比较简单,第四题也有很多的暴力分,相信参加了的各位 OIer 在 2016 年都取得了很好的成绩。

那么,我将会分析 NOIP2016 普及组的四道题目,希望对各位的 OI 学习有所帮助!

T1. 买铅笔

题目描述

\(P\)老师需要去商店买\(n\)支铅笔作为小朋友们参加\(NOIP\)的礼物。她发现商店一共有\(3\)种包装的铅笔,不同包装内的铅笔数量有可能不同,价格也有可能不同。为了公平起见,\(P\)老师决定只买同一种包装的铅笔。

商店不允许将铅笔的包装拆开,因此\(P\)老师可能需要购买超过\(n\)支铅笔才够给小朋友们发礼物。

现在\(P\)老师想知道,在商店每种包装的数量都足够的情况下,要买够至少\(n\)支铅笔最少需要花费多少钱。

输入格式

第一行包含一个正整数\(n\),表示需要的铅笔数量。

接下来三行,每行用\(2\)个正整数描述一种包装的铅笔:其中第\(1\)个整数表示这种包装内铅笔的数量,第\(2\)个整数表示这种包装的价格。

保证所有的\(7\)个数都是不超过\(10000\)的正整数。

输出格式

\(1\)个整数,表示\(P\)老师最少需要花费的钱。

输入输出样例

样例1

输入样例#1:

57
2 2
50 30
30 27

输出样例#1:

54

样例2

输入样例#2:

9998
128 233
128 2333
128 666

输出样例#2:

18407

样例3

输入样例#3:

9999
101 1111
1 9999
1111 9999

输出样例#3:

89991

说明

铅笔的三种包装分别是:

  • \(2\)支装,价格为\(2\);
  • \(50\)支装,价格为\(30\);
  • \(30\)支装,价格为\(27\)。

\(P\)老师需要购买至少\(57\)支铅笔。

如果她选择购买第一种包装,那么她需要购买\(29\)份,共计\(2 \times 29 = 58\)支,需要花费的钱为\(2 \times 29 = 58\)。

实际上,\(P\)老师会选择购买第三种包装,这样需要买\(2\)份。虽然最后买到的铅笔数量更多了,为\(30 \times 2 = 60\)支,但花费却减少为\(27 \times 2 = 54\),比第一种少。

对于第二种包装,虽然每支铅笔的价格是最低的,但要够发必须买\(2\)份,实际的花费达到了 \(30 \times 2 = 60\),因此\(P\)老师也不会选择。

所以最后输出的答案是\(54\)。

题解

T1 总是这么水。

直接暴力判断每种情况,最后输出最小值即可。

代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cctype> using namespace std; inline int gi()
{
int f = 1, x = 0;
char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-')
f = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
{
x = x * 10 + c - '0';
c = getchar();
}
return f * x;
} int a, b, n, c, a1, b1, c1, s1, s2, s3, k = 2147483647; int main()
{
n = gi(), a = gi(), a1 = gi(), b = gi(), b1 = gi(), c = gi(), c1 = gi();
if (n % a == 0)
s1 = a1 * (n / a);
if (n % b == 0)
s2 = b1 * (n / b);
if (n % c == 0)
s3 = c1 * (n / c);
if (n % a != 0)
s1 = a1 * (n / a + 1);
if (n % a != 0)
s2 = b1 * (n / b + 1);
if (n % a != 0)
s3 = c1 * (n / c + 1);
if (k > s1)
k = s1;
if (k > s2)
k = s2;
if (k > s3)
k = s3;
printf("%d\n", k);
return 0;
}

T2. 回文日期

题目描述

在日常生活中,通过年、月、日这三个要素可以表示出一个唯一确定的日期。

牛牛习惯用\(8\)位数字表示一个日期,其中,前\(4\)位代表年份,接下来\(2\)位代表月份,最后\(2\)位代表日期。显然:一个日期只有一种表示方法,而两个不同的日期的表示方法不会相同。

牛牛认为,一个日期是回文的,当且仅当表示这个日期的\(8\)位数字是回文的。现在,牛牛想知道:在他指定的两个日期之间包含这两个日期本身),有多少个真实存在的日期是回文的。

一个\(8\)位数字是回文的,当且仅当对于所有的\(i ( 1 \le i \le 8)\)从左向右数的第\(i\)个数字和第\(9-i\)个数字(即从右向左数的第\(i\)个数字)是相同的。

例如:

  • 对于\(2016\)年\(11\)月\(19\)日,用\(8\)位数字\(20161119\)表示,它不是回文的。

  • 对于\(2010\)年\(1\)月\(2\)日,用\(8\)位数字\(20100102\)表示,它是回文的。

  • 对于\(2010\)年\(10\)月\(2\)日,用\(8\)位数字\(20101002\)表示,它不是回文的。

每一年中都有\(12\)个月份:

其中,\(1,3,5,7,8,10,12\)月每个月有\(31\)天;\(4,6,9,11\)月每个月有\(30\)天;而对于\(2\)月,闰年时有\(29\)天,平年时有\(28\)天。

一个年份是闰年当且仅当它满足下列两种情况其中的一种:

  1. 这个年份是\(4\)的整数倍,但不是\(100\)的整数倍;

  2. 这个年份是\(400\)的整数倍。

例如:

  • 以下几个年份都是闰年:\(2000,2012,2016\)。

  • 以下几个年份是平年:\(1900,2011,2014\)。

输入格式

两行,每行包括一个\(8\)位数字。

第一行表示牛牛指定的起始日期。

第二行表示牛牛指定的终止日期。

保证\(date\_i\)和都是真实存在的日期,且年份部分一定为\(4\)位数字,且首位数字不为\(0\)。

保证\(date1\)一定不晚于\(date2\)。

输出格式

一个整数,表示在\(date1\)和\(date2\)之间,有多少个日期是回文的。

输入输出样例

样例1

输入样例#1:

20110101
20111231

输出样例#1:

1

样例2

输入样例#2:

20000101
20101231

输出样例#2:

2

说明

样例说明

对于样例\(1\),符合条件的日期是\(20111102\)。

对于样例\(2\),符合条件的日期是\(20011002\)和\(20100102\)。

子任务

对于\(60\%\)的数据,满足\(date1 = date2\)。

题解

T2 也很简单

直接枚举年份,然后依次暴力判断即可。

代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cctype> using namespace std; inline int gi()
{
int f = 1, x = 0;
char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-')
f = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
{
x = x * 10 + c - '0';
c = getchar();
}
return f * x;
} int date1, date2, i, j, k, l, a, b, a1, b1; inline int hw(int m)
{
int n = 0;
while (m != 0)
{
n = n * 10 + m % 10;
m /= 10;
}
return n;
} inline int pd(int m, int n)
{
int y = m / 100, r = m % 100;
if (y <= 0 || y > 12)
return 0;
if (r <= 0 || r > 31)
return 0;
if ((y == 4 || y == 6 || y == 9 || y == 11) && r > 30)
return 0;
if (y == 2 && r > 29)
return 0;
return 1;
} int main()
{
date1 = gi(), date2 = gi();
a = date1 / 10000;
a1 = date1 % 10000;
b = date2 / 10000;
b1 = date2 % 10000;
if (date1 == date2)
{
date1 = date1 % 10000;
date2 = date2 / 10000;
for (i = 1; i <= 4; i++)
{
l = l * 10 + date1 % 10;
date1 /= 10;
}
if (l == date2)
{
printf("1");
return 0;
}
else
{
printf("0");
return 0;
}
}
if (hw(a) >= a1 && pd(hw(a), a))
k++;
if (hw(b) <= b1 && pd(hw(b), b) && a != b)
k++;
for (i = a + 1; i < b; i++)
{
if (pd(hw(i), i))
k++;
}
printf("%d\n", k);
return 0;
}

T3. 海港

题目描述

小 K 是一个海港的海关工作人员,每天都有许多船只到达海港,船上通常有很多来自不同国家的乘客。

小 K 对这些到达海港的船只非常感兴趣,他按照时间记录下了到达海港的每一艘船只情况;对于第i艘到达的船,他记录了这艘船到达的时间\(t_{i}\) (单位:秒),船上的乘 客数\(k_i\)​,以及每名乘客的国籍\(x_{i,1}, x_{i,2},…,x_{i,k}\)。

小 K 统计了\(n\)艘船的信息,希望你帮忙计算出以每一艘船到达时间为止的\(24\)小时(\(24\)小时\(=\)\(86400\)秒)内所有乘船到达的乘客来自多少个不同的国家。

形式化地讲,你需要计算\(n\)条信息。对于输出的第\(i\)条信息,你需要统计满足\(t_i-86400<t_p \le t_i\)​的船只\(p\),在所有的\(x_{p,j}\)​中,总共有多少个不同的数。

输入输出格式

输入格式

第一行输入一个正整数\(n\),表示\(小K\)统计了\(n\)艘船的信息。

接下来\(n\)行,每行描述一艘船的信息:前两个整数\(t_i\)​和\(k_i\)​分别表示这艘船到达海港的时间和船上的乘客数量,接下来\(k_i\)​个整数\(x_{i,j}\)表示船上乘客的国籍。

保证输入的\(t_i\)​是递增的,单位是秒;表示从\(小K\)第一次上班开始计时,这艘船在第\(t_i\)秒到达海港。

输出格式

输出\(n\)行,第\(i\)行输出一个整数表示第\(i\)艘船到达后的统计信息。

输入输出样例

样例1

输入样例#1:

3
1 4 4 1 2 2
2 2 2 3
10 1 3

输出样例#1:

3
4
4

样例2

输入样例#2:

4
1 4 1 2 2 3
3 2 2 3
86401 2 3 4
86402 1 5

输出样例#2:

3
3
3
4

说明

样例解释1

第一艘船在第\(1\)秒到达海港,最近\(24\)小时到达的船是第一艘船,共有\(4\)个乘客, 分别是来自国家\(4,1,2,2\),共来自\(3\)个不同的国家;

第二艘船在第\(2\)秒到达海港,最近\(24\)小时到达的船是第一艘船和第二艘船,共有\(4 + 2 = 6\)个乘客,分别是来自国家\(4,1,2,2,2,3\),共来自\(4\)个不同的国家;

第三艘船在第\(10\)秒到达海港,最近\(24\)小时到达的船是第一艘船、第二艘船和第 三艘船,共有\(4+ 2+1=7\)个乘客,分别是来自国家\(4,1,2,2,2,3,3\),共来自4个不同的国家。

样例解释2

第一艘船在第\(1\)秒到达海港,最近\(24\)小时到达的船是第一艘船,共有\(4\)个乘客,分别是来自国家\(1,2,2,3\),共来自\(3\)个不同的国家。

第二艘船在第\(3\)秒到达海港,最近\(24\)小时到达的船是第一艘船和第二艘船,共有\(4+2=6\)个乘客,分别是来自国家\(1,2,2,3,2,3\),共来自\(3\)个不同的国家。

第三艘船在第\(86401\)秒到达海港,最近\(24\)小时到达的船是第二艘船和第三艘船,共有\(2+2=4\)个乘客,分别是来自国家\(2,3,3,4\),共来自\(3\)个不同的国家。

第四艘船在第\(86402\)秒到达海港,最近\(24\)小时到达的船是第二艘船、第三艘船和第四艘船,共有\(2+2+1=5\)个乘客,分别是来自国家\(2,3,3,4,5\),共来自\(4\)个不同的国家。

数据范围

\(1 \le n \le 10^5\),\(\sum{ki} \le 3*10^5\) ,\(1\le x(i,j) \le 10^5\), \(1 \le t(i-1)\le ti \le 10^9\)。

其中\(\sum{ki}\)表示所有的\(k_i\)​的和。

题解

T3 是个模拟水题。

用队列维护当前时间内的乘客,并且统计出现过的国籍数,超过 24h 的就去掉。注意要一边输入一边统计。

最后输出即可。

代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cctype> using namespace std; inline int gi()
{
int f = 1, x = 0;
char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-')
f = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
{
x = x * 10 + c - '0';
c = getchar();
}
return f * x;
} int t[100010], p[100010], q[100010], w[500010], n, s, head, tail, rs, i, j, k, l, r; int main()
{
n = gi();
p[1] = 1;
head = 1;
for (i = 1; i <= n; i++)
{
t[i] = gi(), r = gi();
p[i + 1] = p[i] + r;
for (j = 1; j <= r; j++)
{
if (!q[w[j + p[i] - 1] = gi()]++)
{
s++;
}
}
while (t[i] - t[head] >= 86400)
{
for (k = p[head]; k < p[head + 1]; k++)
{
if (--q[w[k]] == 0)
{
s--;
}
}
head++;
}
printf("%d\n", s);
}
return 0;
}

T4. 魔法阵

题目描述

六十年一次的魔法战争就要开始了,大魔法师准备从附近的魔法场中汲取魔法能量。

大魔法师有\(m\)个魔法物品,编号分别为\(1,2,...,m\)。每个物品具有一个魔法值,我们用\(X_i\)​表示编号为\(i\)的物品的魔法值。每个魔法值\(X_i\)是不超过\(n\)的正整数,可能有多个物品的魔法值相同。

大魔法师认为,当且仅当四个编号为\(a,b,c,d\)的魔法物品满足\(x_a<x_b<x_c<x_d,X_b-X_a=2(X_d-X_c)\),并且\(x_b-x_a<(x_c-x_b)/3\)时,这四个魔法物品形成了一个魔法阵,他称这四个魔法物品分别为这个魔法阵的\(A\)物品,\(B\)物品,\(C\)物品,\(D\)物品。

现在,大魔法师想要知道,对于每个魔法物品,作为某个魔法阵的\(A\)物品出现的次数,作为\(B\)物品的次数,作为\(C\)物品的次数,和作为\(D\)物品的次数。

输入输出格式

输入格式

第一行包含两个空格隔开的正整数\(n,m\)。

接下来\(m\)行,每行一个正整数,第\(i+1\)行的正整数表示\(X_i\)​,即编号为\(i\)的物品的魔法值。

保证\(1 \le n \le 15000\),\(1 \le m \le 40000\),\(1 \le Xi \le n\)。每个\(X_i\)​是分别在合法范围内等概率随机生成的。

输出格式

共\(m\)行,每行\(4\)个整数。第\(i\)行的\(4\)个整数依次表示编号为\(i\)的物品作为\(A,B,C,D\)物品分别出现的次数。

保证标准输出中的每个数都不会超过\(10^9\)。每行相邻的两个数之间用恰好一个空格隔开。

输入输出样例

样例1

输入样例#1:

30 8
1
24
7
28
5
29
26
24

输出样例#1:

4 0 0 0
0 0 1 0
0 2 0 0
0 0 1 1
1 3 0 0
0 0 0 2
0 0 2 2
0 0 1 0

样例2

输入样例#2:

15 15
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

输出样例#2:

5 0 0 0
4 0 0 0
3 5 0 0
2 4 0 0
1 3 0 0
0 2 0 0
0 1 0 0
0 0 0 0
0 0 0 0
0 0 1 0
0 0 2 1
0 0 3 2
0 0 4 3
0 0 5 4
0 0 0 5

说明

样例解释1

共有\(5\)个魔法阵,分别为:

物品\(1,3,7,6\),其魔法值分别为\(1,7,26,29\);

物品\(1,5,2,7\),其魔法值分别为\(1,5,24,26\);

物品\(1,5,7,4\),其魔法值分别为\(1,5,26,28\);

物品\(1,5,8,7\),其魔法值分别为\(1,5,24,26\);

物品\(5,3,4,6\),其魔法值分别为\(5,7,28,29\)。

以物品\(5\)为例,它作为\(A\)物品出现了\(1\)次,作为\(B\)物品出现了\(3\)次,没有作为\(C\)物品或者\(D\)物品出现,所以这一行输出的四个数依次为\(1,3,0,0\)。

此外,如果我们将输出看作一个\(m\)行\(4\)列的矩阵,那么每一列上的\(m\)个数之和都应等于魔法阵的总数。所以,如果你的输出不满足这个性质,那么这个输出一定不正确。你可以通过这个性质在一定程度上检查你的输出的正确性。

题解

这题纯暴力有 40 分,稍加优化就可以得到 65 分的高分。

正解其实是推公式,运用了数学知识。

公式在这里就不手推了,留给读者思考的空间,也可以参考网上的题解。

代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cctype> using namespace std; inline int gi()
{
int f = 1, x = 0;
char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-')
f = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
{
x = x * 10 + c - '0';
c = getchar();
}
return f * x;
} int s, n, m, num[40010], v[40010], a[40010], b[40010], c[40010], d[40010], i, j, l, k, t, va, vb, vc, vd; int main()
{
n = gi(), m = gi();
for (i = 1; i <= m; i++)
{
v[i] = gi();
num[v[i]]++;
}
for (t = 1; t * 9 < n; t++)
{
s = 0;
for (vd = 9 * t + 2; vd <= n; vd++)
{
vc = vd - t;
vb = vc - 6 * t - 1;
va = vb - 2 * t;
s = s + num[vb] * num[va];
c[vc] = c[vc] + num[vd] * s;
d[vd] = d[vd] + num[vc] * s;
}
s = 0;
for (va = n - 9 * t - 1; va >= 1; va--)
{
vb = va + 2 * t;
vc = vb + 6 * t + 1;
vd = vc + t;
s = s + num[vc] * num[vd];
a[va] = a[va] + num[vb] * s;
b[vb] = b[vb] + num[va] * s;
}
}
for (i = 1; i <= m; i++)
{
printf("%d %d %d %d\n", a[v[i]], b[v[i]], c[v[i]], d[v[i]]);
}
return 0;
}

总结

这套题总体来说并不难,只要您合理分配时间、认真思考,就应该能得到 100+100+100+40=340 的高分(除了某些考场上手推数学公式的数学大佬 T4 可以拿满分)。

T1 和 T2 考察了选手的语言基础,只要细心就可以拿全 200 分。

T3 考验了选手的耐心和分析能力,在处理细节方面要注意:细节决定成败!

T4 就是推数学公式,暴力分也很多,考察了选手的数学能力。

希望大家在以后的 OI 道路上能够顺风顺水!

NOIP2016普及组解题报告的更多相关文章

  1. NOIP2016提高组解题报告

    NOIP2016提高组解题报告 更正:NOIP day1 T2天天爱跑步 解题思路见代码. NOIP2016代码整合

  2. NOIP2015&2016普及组解题报告

    NOIP2015普及组题目下载 NOIP2016普及组题目下载 NOIP2015普及组题目: NOIP2018RP++ NOIP2016普及组题目 NOIP2018RP++ T1 金币\((coin. ...

  3. NOIP 2018 普及组 解题报告

    目录 标题统计 题目链接 思路 代码 龙虎斗 题目链接: 思路 代码 摆渡车 题目链接: 思路 对称二叉树 题目链接 思路: 先来解释一下为毛现在才来发解题报告: 其实博主是参加过NOIP 2018普 ...

  4. NOIP2017普及组解题报告

    刚参加完NOIP2017普及,只考了210,于是心生不爽,写下了这篇解题报告...(逃 第一次写博,望dalao们多多指导啊(膜 第一题score,学完helloworld的人也应该都会吧,之前好多人 ...

  5. NOIP2016普及组复赛解题报告

    提高组萌新,DAY1DAY2加起来骗分不到300,写写普及组的题目聊以自慰. (附:洛谷题目链接 T1:https://www.luogu.org/problem/show?pid=1909 T2:h ...

  6. [题解]noip2016普及组题解和心得

    [前言] 感觉稍微有些滑稽吧,毕竟每次练的题都是提高组难度的,结果最后的主要任务是普及组抱一个一等奖回来.至于我的分数嘛..还是在你看完题解后写在[后记]里面.废话不多说,开始题解. 第一题可以说的内 ...

  7. NOIP2016普及组

    普及组.代码有空发. 第一题就是买铅笔.暴力模拟绝对可取. 第二题就是回文日期.从t1的年份到t2的年份枚举每一年.头尾要特判. 第三题就是海港.骗了40分. 第四题就是魔法阵.不太好优化. 完.

  8. noip2016普及组题解和心得

    前言 感觉稍微有些滑稽吧,毕竟每次练的题都是提高组难度的,结果最后的主要任务是普及组抱一个一等奖回来.至于我的分数嘛..还是在你看完题解后写在[后记]里面.废话不多说,开始题解. (其实这篇博客只有题 ...

  9. 【做题记录】[NOIP2016 普及组] 魔法阵

    P2119 魔法阵 2016年普及组T4 题意: 给定一系列元素 \(\{X_i\}\) ,求满足以下不等式的每一个元素作为 \(a,b,c,d\) 的出现次数 . \[\begin{cases}X_ ...

随机推荐

  1. 2019牛客多校第二场H题(悬线法)

    把以前的题补补,用悬线求面积第二大的子矩形.我们先求出最大子矩阵的面积,并记录其行三个方向上的悬线长度.然后排除这个矩形,记得还得特判少一行或者少一列的情况 #include <bits/std ...

  2. CF1237F Balanced Domino Placements

    题意 给定一个 \(h\) 行 \(w\) 列的方格图,上面已经放置了一些 \(1\times 2\) 的多米诺骨牌. 我们称一个放置多米诺骨牌的方案是好的,当且仅当任何两个多米诺骨牌不占用相同的行与 ...

  3. css 字体旋转

    <div v-else> <img src="/img/dashboard/nodata.png"> <div style="color: ...

  4. 问题 E: Problem B

    #include <cstdio> #include <cstring> #include <algorithm> #include <vector> ...

  5. H5_0017:通过元素自定义属性值获取元素对象,并获取属性值

            // 通过元素的属性值查找对象         // document.querySelectorAll("[data]").forEach(function(e) ...

  6. vjudge I - Vladik and fractions 一道小学生的提。

    原题链接:https://vjudge.net/contest/331993#problem/I Vladik and Chloe decided to determine who of them i ...

  7. ubuntu set up 3 - cuda

    https://www.pugetsystems.com/labs/hpc/How-to-install-CUDA-9-2-on-Ubuntu-18-04-1184/ How to install C ...

  8. codeforces刷题记录

    Codefest 19 (open for everyone, rated, Div. 1 + Div. 2) C. Magic Grid 这种题直接构造 数n是2的n次方的倍数的时候可以这样划分数 ...

  9. 占位 SC

    占位 SC include: SC404 SC405

  10. RN开发-Navigator

    1.在入口组件render方法中返回<Navigator>            let defaultName = 'Welcome';            let defaultCo ...