上海市计算机学会竞赛平台 2024 年 7 月月赛 丙组详解

                                                                        \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \text{}                                                                         T1 求和问题

内存限制:256   Mb     时间限制:1000   ms \color{#999}{\footnotesize{\textsf{{内存限制:256 Mb \ \ 时间限制:1000 ms}}}} 内存限制:256 Mb   时间限制:1000 ms

题目描述

给定 n n n 个整数 a 1 , a 2 , … , a n a_1, a_2, \dots, a_n a1,a2,,an,请问这个序列最长有多少长的前缀,满足元素的和大于或等于 0 0 0?如果任何长度大于 0 0 0 的前缀之和都为负数,则输出 0 0 0

输入格式

  • 第一行:单个整数表示 n n n
  • 第二行: n n n 个整数表示 a 1 , a 2 , … , a n a_1, a_2, \dots, a_n a1,a2,,an

输出格式

  • 单个整数:表示最长的前缀长度,使得前缀的和大于等于 0 0 0

数据范围

  • 对于 30 % 30\% 30% 的数据, 1 ≤ n ≤ 100 1\leq n \leq 100 1n100
  • 对于 60 % 60\% 60% 的数据, 1 ≤ n ≤ 10 , 000 1\leq n \leq 10,000 1n10,000
  • 对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 200 , 000 , − 10 , 000 ≤ a i ≤ 10 , 000 1\leq n \leq 200,000, -10,000 \leq a_i \leq 10,000 1n200,000,10,000ai10,000

样例数据

输入 #1

3
1 2 3

输出 #1

3


输入 #2

5
1 2 -1 3 -6

输出 #2

4

解题思路

我们一边输入一边处理,定义一个变量 s s s 累加 a i a_i ai,再根据 s ≥ 0 s \geq 0 s0 考虑是否更新答案。

总时间复杂度 O ( n ) \mathcal{O}(n) O(n)

难度: 入门 \colorbox{FE4C61}{\color{FFFFFF}入门} 入门

算法:……

CODE:

#include <bits/stdc++.h>
using namespace std;
#define int long long
template <typename T>
inline void read(T &x) {
	int f = 1, k = 0;
	char c = getchar();
	while (c < '0' || c > '9') {
		if (c == '-')
			f = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9') k = (k << 1) + (k << 3) + (c ^ 48), c = getchar();
	k *= f;
	x = k;
}
template <typename T, typename... Args>
inline void read(T &tmp, Args &... tmps) {
	read(tmp);
	read(tmps...);
}

inline void write(int x) {
	if (x < 0)
		putchar('-'), x = -x;
	if (x > 9)
		write(x / 10);
	putchar(x % 10 + '0');
	return;
}
inline void write(string s) {
	int len = s.size();
	for (int i = 0; i < len; i++) putchar(s[i]);
	putchar('\n');
}
//-----------------------------------------------------
//这上面都不用看,只需要知道 read 是读入,write 是输出即可。

signed main() {
	ios::sync_with_stdio(false);
	ios_base::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	int n;
	read(n);
	int sum = 0, k = 0;
	for (int i = 1; i <= n; i++) {
		int a;
		read(a);
		sum += a;
		if (sum >= 0) {
			k = i;
		}
	}
	write(k);
	return 0;
}

                                                                        \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \text{}                                                                         T2 得分排名

内存限制:256   Mb     时间限制:1000   ms \color{#999}{\footnotesize{\textsf{{内存限制:256 Mb \ \ 时间限制:1000 ms}}}} 内存限制:256 Mb   时间限制:1000 ms

题目描述

给定 n n n 名学生的考试得分,这些学生的学号为 1 ∼ n 1\sim n 1n,其中第 i i i 号学生的得分为 a i a_i ai,请将这些学生按照分数从大到小的顺序排列并输出学号序列。

输入格式

  • 第一行:单个整数表示 n n n
  • 第二行: n n n 个整数表示 a 1 , a 2 , … , a n a_1, a_2, \dots, a_n a1,a2,,an

输出格式

  • n n n 行,每行数字表示相对排名的学号。

数据范围

  • 对于 30 % 30\% 30% 的数据, 1 ≤ n ≤ 100 1\leq n \leq 100 1n100
  • 对于 60 % 60\% 60% 的数据, 1 ≤ n ≤ 3000 1\leq n \leq 3000 1n3000
  • 对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 200 , 000 , 0 ≤ a i ≤ 1 , 000 , 000 , 000 1\leq n \leq 200,000, 0 \leq a_i \leq 1,000,000,000 1n200,000,0ai1,000,000,000

样例数据

输入 #1

3
60 60 100

输出 #1

3
1
2


解题思路

我们用结构体将 i d , s c o r e id,score id,score 绑在一起,然后再进行结构体排序,最后输出学号即可。

总时间复杂度 O ( n log ⁡ n ) \mathcal{O}(n \log n) O(nlogn)

难度: 入门 \colorbox{FE4C61}{\color{FFFFFF}入门} 入门

算法: 结构体 排序 \colorbox{294AB5}{\color{FFFFFF}结构体}\colorbox{294AB5}{\color{FFFFFF}排序} 结构体排序

CODE:

#include <bits/stdc++.h>
using namespace std;
#define int long long
template <typename T>
inline void read(T &x) {
	int f = 1, k = 0;
	char c = getchar();
	while (c < '0' || c > '9') {
		if (c == '-')
			f = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9') k = (k << 1) + (k << 3) + (c ^ 48), c = getchar();
	k *= f;
	x = k;
}
template <typename T, typename... Args>
inline void read(T &tmp, Args &... tmps) {
	read(tmp);
	read(tmps...);
}

inline void write(int x) {
	if (x < 0)
		putchar('-'), x = -x;
	if (x > 9)
		write(x / 10);
	putchar(x % 10 + '0');
	return;
}
inline void write(string s) {
	int len = s.size();
	for (int i = 0; i < len; i++) putchar(s[i]);
	putchar('\n');
}
//-----------------------------------------------------
//这上面都不用看,只需要知道 read 是读入,write 是输出即可。

struct node {
	int score;
	int id;
	inline bool operator < (const node t) const {   //重载小于号(局部有效),这样可以不用写 cmp。
		if (score != t.score)
			return score > t.score;
		return id < t.id;
	}
} a[200010];
signed main() {
	ios::sync_with_stdio(false);
	ios_base::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	int n;
	read(n);
	for (int i = 1; i <= n; i++)
		read(a[i].score), a[i].id = i;
	sort(a + 1, a + 1 + n);
	for (int i = 1; i <= n; i++)
		write(a[i].id), putchar('\n');
	return 0;
}

                                                                 \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \text{}                                                                  T3 录制节目(一)

内存限制:256   Mb     时间限制:1000   ms \color{#999}{\footnotesize{\textsf{{内存限制:256 Mb \ \ 时间限制:1000 ms}}}} 内存限制:256 Mb   时间限制:1000 ms

题目描述

电视里将要播放 n n n 个节目,第 i i i 个节目从时刻 s i s_i si 开始,到 t i t_i ti 结束,没有回放。小爱有一台录像机,一台录像机在工作的时候只能录一个节目,小爱最多可以录下多少完整的节目呢?

如果某节目的结束时间等于另一个节目的开始时间,那么这两个节目是可以用一台录像机的。

输入格式

  • 第一行:单个整数表示 n n n
  • 第二行到第 n + 1 n + 1 n+1 行:第 i + 1 i + 1 i+1 行有两个整数 s i , t i s_i, t_i si,ti

输出格式

  • 单个整数:表示最大可以录制的节目数量。

数据范围

  • 对于 30 % 30\% 30% 的数据, 1 ≤ n ≤ 500 1\leq n \leq 500 1n500
  • 对于 60 % 60\% 60% 的数据, 1 ≤ n ≤ 2000 1\leq n \leq 2000 1n2000
  • 对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 200 , 000 , 0 ≤ s i , t i ≤ 1 , 000 , 000 , 000 1\leq n \leq 200,000, 0 \leq s_i, t_i \leq 1,000,000,000 1n200,000,0si,ti1,000,000,000

样例数据

输入 #1

3
6 9
1 5
2 8

输出 #1

2


解题思路

我们用结构体将 s i , t i s_i, t_i si,ti 绑在一起,然后用结构体以 t t t 为关键字排序。

然后对于每个节目,我们看看当前我们录制的节目的结束时间是否与现在我们想要录制的节目的开始时间有冲突,如果是,那么这个节目不可录制,否则可以录制。

总时间复杂度 O ( n log ⁡ n ) \mathcal{O}(n \log n) O(nlogn)

难度: 普及- \colorbox{F39C11}{\color{FFFFFF}普及-} 普及-

算法: 结构体 排序 贪心 \colorbox{294AB5}{\color{FFFFFF}结构体}\colorbox{294AB5}{\color{FFFFFF}排序}\colorbox{294AB5}{\color{FFFFFF}贪心} 结构体排序贪心

CODE:

#include <bits/stdc++.h>
using namespace std;
#define int long long
template <typename T>
inline void read(T &x) {
	int f = 1, k = 0;
	char c = getchar();
	while (c < '0' || c > '9') {
		if (c == '-')
			f = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9') k = (k << 1) + (k << 3) + (c ^ 48), c = getchar();
	k *= f;
	x = k;
}
template <typename T, typename... Args>
inline void read(T &tmp, Args &... tmps) {
	read(tmp);
	read(tmps...);
}

inline void write(int x) {
	if (x < 0)
		putchar('-'), x = -x;
	if (x > 9)
		write(x / 10);
	putchar(x % 10 + '0');
	return;
}
inline void write(string s) {
	int len = s.size();
	for (int i = 0; i < len; i++) putchar(s[i]);
	putchar('\n');
}
//-----------------------------------------------------
//这上面都不用看,只需要知道 read 是读入,write 是输出即可。

struct node {
	int s, t;
	inline bool operator < (const node a) const {
		return t < a.t;
	}
}a[200010];
signed main() {
	ios::sync_with_stdio(false);
	ios_base::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	int n;
	read(n);
	for (int i = 1; i <= n; i++)
		read(a[i].s, a[i].t);
	sort(a + 1, a + 1 + n);
	int ans = 0, k = 0;
	for (int i = 1; i <= n; i++) {
		if (a[i].s >= k) {
			k = a[i].t;
			ans++;
		}
	}
	cout << ans;
	return 0;
}

                                                                        \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \text{}                                                                         T4 子集归零

内存限制:256   Mb     时间限制:1000   ms \color{#999}{\footnotesize{\textsf{{内存限制:256 Mb \ \ 时间限制:1000 ms}}}} 内存限制:256 Mb   时间限制:1000 ms

题目描述

给定 n n n 个数字 a 1 , a 2 , … , a n a_1, a_2, \dots, a_n a1,a2,,an,请统计能从 1 ∼ n 1\sim n 1n 中,选出多少种不同的下标子集,使得这些下标对应的数字之和等于 0 0 0

注意空集与全集也是子集中的一种。

输入格式

  • 第一行:单个整数表示 n n n
  • 第二行: n n n 个整数表示 a 1 , a 2 , … , a n a_1, a_2, \dots, a_n a1,a2,,an

输出格式

  • 单个整数:表示归零子集的数量。

数据范围

  • 对于 30 % 30\% 30% 的数据, 1 ≤ n ≤ 5 1\leq n \leq 5 1n5
  • 对于 60 % 60\% 60% 的数据, 1 ≤ n ≤ 10 1\leq n \leq 10 1n10
  • 对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 22 , − 1 , 000 , 000 ≤ a i ≤ 1 , 000 , 000 1\leq n \leq 22, -1,000,000 \leq a_i \leq 1,000,000 1n22,1,000,000ai1,000,000

样例数据

输入 #1

4
2 -1 -2 1

输出 #1

4

说明 #1

{}
{1 -1}
{2 -2}
{1 2 -1 -2}


解题思路

我们直接 dfs 爆搜,考虑第 i i i 个数字选不选进集合内,最后统计选进集合内的数的和,判断其是否为 0 0 0 即可。

总时间复杂度 O ( 2 n ) \mathcal{O}(2^n) O(2n)

难度: 普及- \colorbox{F39C11}{\color{FFFFFF}普及-} 普及-

算法: 深度优先搜索,DFS \colorbox{294AB5}{\color{FFFFFF}深度优先搜索,DFS} 深度优先搜索,DFS

CODE:

#pragma GCC optimize(3)
#pragma GCC optimize("-funsafe-loop-optimizations")
#pragma GCC optimize("-funroll-loops")
#pragma GCC optimize("-fwhole-program")
#pragma GCC optimize("Ofast,no-stack-protector")
#pragma GCC optimize("-fthread-jumps")
#pragma GCC optimize("-falign-functions")
#pragma GCC optimize("-falign-jumps")
#pragma GCC optimize("-falign-loops")
#pragma GCC optimize("-falign-labels")
#pragma GCC optimize("-fcaller-saves")
#pragma GCC optimize("-fcrossjumping")
#pragma GCC optimize("-fcse-follow-jumps")
#pragma GCC optimize("-fcse-skip-blocks")
#pragma GCC optimize("-fdelete-null-pointer-checks")
#pragma GCC optimize("-fdevirtualize")
#pragma GCC optimize("-fdevirtualize-speculatively")
#pragma GCC optimize("-fexpensive-optimizations")
#pragma GCC optimize("-fgcse")
#pragma GCC optimize("-fgcse-lm")
#pragma GCC optimize("-fhoist-adjacent-loads")
#pragma GCC optimize("-finline-small-functions")
#pragma GCC optimize("-findirect-inlining")
#pragma GCC optimize("-fipa-sra")
#pragma GCC optimize("-fisolate-erroneous-paths-dereference")
#pragma GCC optimize("-foptimize-sibling-calls")
#pragma GCC optimize("-fpartial-inlining")
#pragma GCC optimize("-fpeephole2")
#pragma GCC optimize("-freorder-blocks")
#pragma GCC optimize("-freorder-functions")
#pragma GCC optimize("-frerun-cse-after-loop")
#pragma GCC optimize("-fsched-interblock")
#pragma GCC optimize("-fsched-spec")
#pragma GCC optimize("-fschedule-insns")
#pragma GCC optimize("-fschedule-insns2")
#pragma GCC optimize("-fstrict-aliasing")
#pragma GCC optimize("-fstrict-overflow")
#pragma GCC optimize("-ftree-switch-conversion")
#pragma GCC optimize("-ftree-tail-merge")
#pragma GCC optimize("-ftree-pre")
#pragma GCC optimize("-ftree-vrp")
#pragma GCC target("avx") 
//火车头 + O2 + O3 优化,本来以为 dfs 爆搜跑不过,结果 iai 的机子挺快的,上面部分可以不用
#include <bits/stdc++.h>
using namespace std;
#define int long long
template <typename T>
inline void read(T &x) {
	int f = 1, k = 0;
	char c = getchar();
	while (c < '0' || c > '9') {
		if (c == '-')
			f = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9') k = (k << 1) + (k << 3) + (c ^ 48), c = getchar();
	k *= f;
	x = k;
}
template <typename T, typename... Args>
inline void read(T &tmp, Args &... tmps) {
	read(tmp);
	read(tmps...);
}

inline void write(int x) {
	if (x < 0)
		putchar('-'), x = -x;
	if (x > 9)
		write(x / 10);
	putchar(x % 10 + '0');
	return;
}
inline void write(string s) {
	int len = s.size();
	for (int i = 0; i < len; i++) putchar(s[i]);
	putchar('\n');
}
//-----------------------------------------------------
//这上面都不用看,只需要知道 read 是读入,write 是输出即可。

int b[30], ans, n, a[30];
inline void dfs(int step, int sum) {
	if (sum == 0) {
		ans++;
	}
	for (int i = b[step - 1] + 1; i <= n; i++) {
		b[step] = i;
		dfs(step + 1, sum + a[i]);
	}
}
signed main() {
	ios::sync_with_stdio(false);
	ios_base::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	read(n);
	for (int i = 1; i <= n; i++) {
		read(a[i]);
	}
	dfs(1, 0);
	write(ans);
	return 0;
}

                                                                        \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \text{}                                                                         T5 池塘计数

内存限制:256   Mb     时间限制:1000   ms \color{#999}{\footnotesize{\textsf{{内存限制:256 Mb \ \ 时间限制:1000 ms}}}} 内存限制:256 Mb   时间限制:1000 ms

题目描述

n × m n \times m n×m 个方格,一些方格是水,用字符 . 表示,一些方格是陆地,用字符 # 表示。

两个方格相邻的定义是这两个方格至少有一个公共的边,每个方格最多有四个相邻的方格。

若相邻的方格都是水,那么就会连成更大一个池塘。请计算给定的地形中有多少个不连通的池塘。

输入格式

  • 第一行:两个整数表示 n , m n, m n,m
  • 第二行到 n + 1 n + 1 n+1 行:第 i + 1 i + 1 i+1 行有 m m m 个字符。

输出格式

  • 单个整数:表示有多少个不连通的池塘。

数据范围

  • 对于 30 % 30\% 30% 的数据, 1 ≤ n , m ≤ 10 1\leq n, m \leq 10 1n,m10
  • 对于 60 % 60\% 60% 的数据, 1 ≤ n , m ≤ 100 1\leq n, m \leq 100 1n,m100
  • 对于 100 % 100\% 100% 的数据, 1 ≤ n , m ≤ 200 1\leq n, m \leq 200 1n,m200

样例数据

输入 #1

3 5
…##
.#.##
###…

输出 #1

2

解题思路

我们使用 bfs 从没走过的水开始向上、下、左、右搜索,遇到走过的水或者陆地就返回,遇到没走过的水我们就标记一下。

每进行一次 bfs 答案加一。

总时间复杂度 O ( n m ) \mathcal{O}(nm) O(nm)

难度: 普及/提高- \colorbox{FFC116}{\color{FFFFFF}普及/提高-} 普及/提高-

算法: 广度优先搜索,BFS 深度优先搜索,DFS \colorbox{294AB5}{\color{FFFFFF}广度优先搜索,BFS} \colorbox{294AB5}{\color{FFFFFF}深度优先搜索,DFS} 广度优先搜索,BFS深度优先搜索,DFS

CODE:

#include <bits/stdc++.h>
using namespace std;
#define int long long
template <typename T>
inline void read(T &x) {
	int f = 1, k = 0;
	char c = getchar();
	while (c < '0' || c > '9') {
		if (c == '-')
			f = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9') k = (k << 1) + (k << 3) + (c ^ 48), c = getchar();
	k *= f;
	x = k;
}
template <typename T, typename... Args>
inline void read(T &tmp, Args &... tmps) {
	read(tmp);
	read(tmps...);
}

inline void write(int x) {
	if (x < 0)
		putchar('-'), x = -x;
	if (x > 9)
		write(x / 10);
	putchar(x % 10 + '0');
	return;
}
inline void write(string s) {
	int len = s.size();
	for (int i = 0; i < len; i++) putchar(s[i]);
	putchar('\n');
}
//-----------------------------------------------------
//这上面都不用看,只需要知道 read 是读入,write 是输出即可。

char c[210][210];
struct node {
	int x, y;
};
queue<node> q;
bool b[210][210];
int dx[] = {0, 0, -1, 1}, n, m;
int dy[] = {1, -1, 0, 0};
inline void bfs(int x, int y) {
	q.push({x, y});
	while (!q.empty()) {
		node t = q.front();
		q.pop();
		for (int i = 0; i < 4; i++) {
			int xx = t.x + dx[i];
			int yy = t.y + dy[i];
			if (xx > 0 && yy > 0 && xx <= n && yy <= m && c[xx][yy] == '.' && !b[xx][yy]) {
				b[xx][yy] = 1;
				q.push({xx, yy});
			}
		}
	}
}
signed main() {
	ios::sync_with_stdio(false);
	ios_base::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	read(n, m);
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			int k = getchar();
			while (k != '.' && k != '#')
				k = getchar();
			c[i][j] = k;
		}
	}
	int ans = 0;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			if (c[i][j] == '.' && !b[i][j]) {
				bfs(i, j);
				ans++;
			}
		}
	}
	write(ans);
	return 0;
}

总结

上海市计算机学会竞赛平台的月赛似乎越来越水了(或者是我变强了)。

上一次参赛还是在 2023 2023 2023 8 , 7 , 6 8,7,6 8,7,6 月,那时候只能拿一、二等奖( 190 ∼ 440 190\sim 440 190440 分),现在可以 AK 了!

所有比赛越来越水自己的实力越来越强。

评论 38
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值