锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

CSP-J 2021 入门组/普及组

时间:2023-12-26 10:07:02 j95连接器

文章目录

  • 整体难度分析
  • T1 分糖果
    • T1分析
  • T2 插入排序
    • T2分析
      • 1. 常规解法
      • 2. 黑科技 pb_ds
  • T3 网络连接
    • T3分析
  • T4 小熊的果篮
    • T4分析

整体难度分析

总的来说,这场比赛 CSP 比赛难度不高,四题基本都是模拟题,至少有 310 310 310 纯暴力可以直接获得分数

但是处理代码量和细节的要求比较高,在 OI 的比赛模式中,很容易因为踩了一个坑导致分数非常低

总的来说,难度不高,题型更倾向于 CF 类的简单题

T1 分糖果

【题目背景】
红太阳幼儿园的孩子们开始分糖果了!

【题目描述】
红太阳幼儿园有 nn 孩子,你就是其中之一。 n \ge 2n≥2。

有一天,你在幼儿园后花园里发现了无尽的糖果,你打算给幼儿园的孩子们带一些糖果。

因为你只是一个普通的幼儿园孩子,你的体力有限,最多只能拿 RR 块糖回去。

但是拿的太少是不够的,所以至少要拿 LL 块糖回去 n \le L \le Rn≤L≤R。

也就是说,如果你拿了, kk 你需要保证块糖 L \le k \le RL≤k≤R。

如果你拿了 kk 块糖,你会把这个拿走的 kk 把块糖放在篮子里,要求你按照以下方案分糖:只要篮子里不少于 nn 所有的糖果,幼儿园 nn 孩子(包括你自己)从篮子里拿走一块糖,直到篮子里的糖少于一块糖 nn 块。篮子里剩下的糖果都属于你——这些糖果是你移动糖果的奖励。

作为幼儿园的优质孩子,你想让你移动糖果的糖果数量(而不是你最终得到的总糖果数量!)尽可能多;所以你需要写一个程序,依次输入 n, L, Rn,L,R,输出你最多能得到多少糖作为你移动糖果的奖励。

输入格式
输入行包括三个正整数 n, L, Rn,L,R,分别表示儿童数量、糖果数量的下界和上界。

输出格式
输出一行一个整数,说明你最多能得到的糖果数量作为你移动糖果的奖励。

【输入输出样】
输入 #1
7 16 23
输出 #1
6
输入 #2
10 14 18
输出 #2
8
说明/提示
【样例解释 #1】

k = 20 k = 20 k=20 把块糖放进篮子里。

现在篮子里的糖果数 20 ≥ n = 7 20 \ge n = 7 20n=7,所以所有的孩子都得到一块糖;

现在篮子里的糖果数变成了 13 ≥ n = 7 13 \ge n = 7 13n=7,所以所有的孩子都得到一块糖;

现在篮子里的糖果数变成了 6 < n = 7 6 < n = 7 6<n=7,因此这 6 6 6 块糖是作为你搬糖果的奖励。

容易发现,你获得的作为你搬糖果的奖励的糖果数量不可能超过 6 6 6 块(不然,篮子里的糖果数量最后仍然不少于 n n n,需要继续每个小朋友拿一块),因此答案是 6 6 6

【样例解释 #2】

容易发现,当你拿的糖数量 k k k 满足 14 = L ≤ k ≤ R = 18 14 = L \le k \le R = 18 14=LkR=18 时,所有小朋友获得一块糖后,剩下的 k − 10 k - 10 k10 块糖总是作为你搬糖果的奖励的糖果数量,因此拿 k = 18 k = 18 k=18 块是最优解,答案是 8 8 8

【数据范围】
在这里插入图片描述

T1分析

送分题,题意就是在区间 [ L , R ] [L,R] [L,R] 内求一个数 k k k 使得 k % n k\%n k%n 最大

那么我们只要找到比 L L L 大的最小的 n n n 的倍数即可

如果这个数字存在于 [ L , R ] [L,R] [L,R] ,那么最后的答案就是 n − 1 n - 1 n1

若这个最小的 n n n 倍数不存在于 [ L , R ] [L,R] [L,R] 内,那么答案就是区间最大值 R % n R \% n R%n

#include 
using namespace std;

int main(){ 
        
	long long n, L, R;
	scanf("%lld%lld%lld", &n, &L, &R);
	long long temp = (L + n + 1) / n * n;
	temp = min(temp - 1, R);
	printf("%lld\n", temp % n);
	return 0;
}

T2 插入排序

【题目描述】
插入排序是一种非常常见且简单的排序算法。小 Z 是一名大一的新生,今天 H 老师刚刚在上课的时候讲了插入排序算法。

假设比较两个元素的时间为 O ( 1 ) \mathcal O(1) O(1),则插入排序可以以 O ( n 2 ) \mathcal O(n^2) O(n2) 的时间复杂度完成长度为 n n n 的数组的排序。不妨假设这 n n n 个数字分别存储在 a 1 , a 2 , … , a n a_1, a_2, \ldots, a_n a1,a2,,an 之中,则如下伪代码给出了插入排序算法的一种最简单的实现方式:
这下面是 C/C++ 的示范代码

for (int i = 1; i <= n; i++)
	for (int j = i; j >= 2; j--)
		if (a[j] < a[j-1]) { 
        
			int t = a[j-1];
			a[j-1] = a[j];
			a[j] = t;
		}

这下面是 Pascal 的示范代码

for i:=1 to n do
	for j:=i downto 2 do
		if a[j]<a[j-1] then
			begin
				t:=a[i];
				a[i]:=a[j];
				a[j]:=t;
			end;

为了帮助小 Z 更好的理解插入排序,小 Z 的老师 H 老师留下了这么一道家庭作业:

H 老师给了一个长度为 n n n 的数组 a a a,数组下标从 1 1 1 开始,并且数组中的所有元素均为非负整数。小 Z 需要支持在数组 a a a 上的 Q Q Q 次操作,操作共两种,参数分别如下:

1   x   v 1\ x\ v 1 x v:这是第一种操作,会将 a a a 的第 x x x 个元素,也就是 a x a_x ax 的值,修改为 v v v。保证 1 ≤ x ≤ n 1 \le x \le n 1xn 1 ≤ v ≤ 1 0 9 1 \le v \le 10^9 1v109注意这种操作会改变数组的元素,修改得到的数组会被保留,也会影响后续的操作

2   x 2\ x 2 x:这是第二种操作,假设 H 老师按照上面的伪代码 a a a 数组进行排序,你需要告诉 H 老师原来 a a a 的第 x x x 个元素,也就是 a x a_x ax,在排序后的新数组所处的位置。保证 1 ≤ x ≤ n 1 \le x \le n 1xn注意这种操作不会改变数组的元素,排序后的数组不会被保留,也不会影响后续的操作

H 老师不喜欢过多的修改,所以他保证类型 1 1 1 的操作次数不超过 5000 5000 5000

小 Z 没有学过计算机竞赛,因此小 Z 并不会做这道题。他找到了你来帮助他解决这个问题。

【输入格式】
第一行,包含两个正整数 n , Q n, Q n,Q,表示数组长度和操作次数。

第二行,包含 n n n 个空格分隔的非负整数,其中第 i i i 个非负整数表示 a i a_i ai

接下来 Q Q Q 行,每行 2 ∼ 3 2 \sim 3 23 个正整数,表示一次操作,操作格式见 【题目描述】

【输出格式】
对于每一次类型为 2 2 2 的询问,输出一行一个正整数表示答案。

【输入输出样例】
输入 #1
3 4
3 2 1
2 3
1 3 2
2 2
2 3
输出 #1
1
1
2
【说明/提示】
【样例解释 #1】

在修改操作之前,假设 H 老师进行了一次插入排序,则原序列的三个元素在排序结束后所处的位置分别是 3 , 2 , 1 3, 2, 1 3,2,1

在修改操作之后,假设 H 老师进行了一次插入排序,则原序列的三个元素在排序结束后所处的位置分别是 3 , 1 , 2 3, 1, 2 3,1,2

注意虽然此时 a 2 = a 3 a_2 = a_3 a2=a3,但是我们不能将其视为相同的元素

【数据范围】

T2分析

1. 常规解法

乍一看题目会以为是一个数据结构的题目,又是单点更新又是查询数字排第几大,会认为是一个平衡树的题目
但是仔细观察以后会发现题目给出了一个很重要的条件:修改次数不超过5000,这是本题的第一个重点

然后需要注意观察样例和题目给出的伪代码,可以得到当数字一样时,下标小的数字被认为更小,这是本题的第二个重点

两者结合,可以得到暴力的解法就是每次修改操作以后都排序一遍,重新求 r a n k rank rank,这样的修改的复杂度是 O ( n l o g n ) O(nlogn) O(nlogn),查询的复杂度是 O ( 1 ) O(1) O(1) ,代码复杂度为 O ( 5000 ∗ n l o g n ) O(5000 * nlogn) O(5000nlogn),可以得到 80 80 80

这里可以发现,每次修改只会修改一个数,那么如果这个数组本身就有序了,当修改过一个数字 a [ x ] a[x] a[x] 以后,无非就是只有两种情况

  1. a [ x ] a[x] a[x] 左移
  2. a [ x ] a[x] a[x] 右移

所以这里完全没有必要重新 s o r t sort sort,只要手动处理一下 a [ x ] a[x] a[x] 的移动即可,这样修改的复杂度就会降为 O ( n ) O(n) O(n),那么整体复杂就是 O ( 5000 ∗ n ) O(5000*n) O(5000n) 完全没有问题

#include 
#include 
using namespace std;
struct XX{ 
        
	int val, id;
} a[100010];
int _rank[100010];

bool cmp(const XX&x, const XX&y){ 
        
	if (x.val == y.val){ 
        
		return x.id < y.id;
	}
	return x.val < y.val;
}
int main(){ 
        
	int n, q;
	scanf("%d%d", &n, &q);
	for (int i = 1; i <= n; ++i){ 
        
		scanf("%d", &a[i].val);
		a[i].id = i;
	}
	sort(a + 1, a + n + 1, cmp);
	for (int i = 1; i <= n; ++i){ 
        
		_rank[a[i].id] = i;
	}
	while (q--){ 
        
		int op, x, y;
		scanf("%d", &op);
		if (op == 1){ 
        
			scanf("%d%d", &x, &y);
			x = _rank[x];
			a[x].val = y;
			while (x > 1 && !cmp(a[x - 1], a[x])){ 
        
				swap(a[x - 1], a[x]);
				x--;
			}
			while (x < n && !cmp(a[x], a[x + 1])){ 
        
				swap(a[x], a[x + 1]);
				x++;
			}
			for (int i = 1; i <= n; ++i){ 
        
				_rank[a[i].id] = i;
			}
		} else { 
        
			scanf("%d", &x);
			printf("%d\n", _rank[x]);
		}
	}
	return 0;
}

2. 黑科技 pb_ds

就算这道题没有 操作数不超过 5000 这个限制,这道题也可以直接用 ext 拓展库 p b _ d s pb\_ds pb_ds 中的 t r e e tree tree 直接解决,复杂度为 O ( ( n + q ) l o g n ) O((n+q)logn) O((n+q)lo元器件数据手册IC替代型号,打造电子元器件IC百科大全!

相关文章