上海市计算机学会2023年5月月赛

T1-三角形的分类

题目描述

给定三个角度 a a a b b b c c c。请判断这三个角在平面上能组成什么样的三角形:

∘ \circ 如果不能组成三角形,输出 Error
∘ \circ 如果能组成等边三角形,输出 Equilateral
∘ \circ 如果能组成等腰直角三角形,输出 Isosceles right
∘ \circ 如果能组成等腰三角形,输出 Isosceles
∘ \circ 如果能组成直角三角形,输出 Right
∘ \circ 如果能组成不等边三角形,输出 Scalene

输入格式

第一行:第一个角的角度 a
第二行:第二个角的角度 b
第三行:第三个角的角度 c

输出格式

根据题目要求输出对应的文字

数据范围

1 ≤ a , b , c ≤ 180 1\leq a,b,c\leq 180 1a,b,c180

样例输入

60
60
60

样例输出

Equilateral

分析

这题只需要用 if 判断一下就行了。

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

int a, b, c;

void swap(int &x, int &y, int &z){ //将a,b,c排序
	if (x > y) swap(x, y);
	if (x > z) swap(x, z);
	if (y > z) swap(y, z);
}

int main(){
	cin >> a >> b >> c;
	swap(a, b, c);
	if (a + b + c != 180) cout << "Error" << endl;
	else if (a == b && b == c) cout << "Equilateral" << endl;
	else if ((a == b || b == c) && a + b == c)
		cout << "Isosceles right" << endl;
	else if (a == b || b == c || a == c) cout << "Isosceles" << endl;
	else if (a + b == c) cout << "Right" << endl;
	else cout << "Scalene" << endl;
	return 0;
}

T2-区间最大公约数

题目描述

给定两个正整数 L , R L,R L,R, 你可以任意选择两个正整数 x , y x,y x,y 且满足 L ≤ x < y ≤ R L \leq x \lt y \leq R Lx<yR,并求出 x , y x,y x,y 的最大公约数。

请问在所有选法中, x , y x,y x,y 最大公约数的最大值为多少?

输入格式

输入共一行,两个正整数表示 L , R L,R L,R

输出格式

输出共一个整数,表示所求答案

数据范围

∘ \circ 对于 30 % 30\% 30%的数据, 1 ≤ L < R ≤ 100 1\leq L \lt R \leq 100 1L<R100
∘ \circ 对于 60 % 60\% 60% 的数据, 1 ≤ L < R ≤ 1 0 3 1\leq L \lt R \leq 10^3 1L<R103
∘ \circ 对于 100 % 100\% 100% 的数据, 1 ≤ L < R ≤ 5 × 1 0 5 1\leq L \lt R \leq 5\times10^5 1L<R5×105

样例输入1

23 29

样例输出2

4

样例说明1

选(24,28)时,gcd取到最大值为 4

样例输入2

32678 65536

样例输出2

32768

样例输入3

32768 32769

样例输出3

1

分析

很显然,枚举区间肯定会超时。那该怎么办呢?我们可以枚举最大公约数的值,从 l l l r r r之间只要可以找到公约数,就把它赋到 a n s ans ans上。我们可以用 f f f来记录左端点,用 e e e记录右端点。如果左端点在右端点的左边,那么 l l l r r r之间就有公约数 i i i的倍数, a n s ans ans直接赋成 i i i就行了。哦对了,左端点有两种情况:一是 l l l可以被 i i i整除,这时 f f f直接赋成 l / i l / i l/i就行了;二是 l l l不能被 i i i整除,因为整型除法会向下取整,所以需要特殊处理: l ÷ i + 1 l \div i + 1 l÷i+1

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

int l, r, ans = 1;

int main(){
	cin >> l >> r;
	int f = 0, e = 0;
	for (int i = 2; i <= r; ++i){
		if (l % i == 0) f = l / i;//左端点
		else f = l / i + 1;
		e = r / i;//右端点
		if (f < e) ans = i;
	}
	cout << ans << endl;
	return 0;
}

T3-滑雪训练

题目描述

小爱最近迷上了滑雪,某滑雪场有 n n n 条不同难度的雪道,只有学习并滑了第 i i i 条雪道,才能去参加第 i + 1 i+1 i+1 条雪道的学习与训练。

已知,第一次滑第 i i i 条雪道时,需要先进行 a i a_i ai 分钟的学习,再花 b i b_i bi 分钟滑该雪道一次,才算学习完成。若之后再滑第 i i i 条雪道,则仅需 b i b_i bi 分钟即可滑一次。

小爱共有 T T T 分钟时间,请问如何安排才能使他能滑的圈数最多?

输入格式

输入第一行,两个正整数 n , T n,T n,T
接下来 n n n 行:每行两个正整数 a i , b i a_i,b_i ai,bi 表示第 i i i条雪道需要的学习时间和滑雪时间。

输出格式

输出一个正整数,表示小爱最多可以滑的圈数。

数据范围

∘ \circ 对于 30 % 30\% 30% 的数据, 1 ≤ n ≤ 10 1\leq n \leq 10 1n10
∘ \circ 对于 60 % 60\% 60% 的数据, 1 ≤ n ≤ 1 0 3 1\leq n \leq 10^3 1n103
∘ \circ 对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 1 0 5 , 1 ≤ a i , b i , T ≤ 1 0 18 1\leq n \leq 10^5,1 \leq a_i,b_i,T \leq 10^{18} 1n1051ai,bi,T1018

样例输入

3 100
10 20
5 5
20 10

样例输出

14

样例说明

先花30分钟学习第一滑道,此时共计滑了一圈
在花10分钟学习第二滑道,此时共计滑了两圈
剩余60分钟,滑第二滑道,共计滑了14圈

分析

因为要求滑最多的圈数,所以我们应该学到滑冰时间最少的圈数,然后剩下的时间全部用来滑这一圈 (花最少的时间,滑最多的圈数) 。那么我们应该怎么实现呢?那就得用——前缀和啦! s i s_i si表示从第1圈滑到第 i i i圈一共要多少时间(只是学习花的时间)。至于 s s s数组该怎么算,就不用我讲了叭 (该不会有人还不会求前缀和吧)
接下来回归正题。我们用循环从 1 − n 1 - n 1n枚举只学到第 i i i圈,接下来之滑第 i i i圈一共能滑多少圈,然后用算出来的数与 a n s ans ans做个比较,谁大就把谁赋到 a n s ans ans里。那么问题来了:该怎么算一共能滑多少圈呢?嗯······我们知道 s i s_i si记录着滑到第 i i i圈共用多少时间,那么就用总时间 t t t减去 s i s_i si,再用求得的值除以 b i b_i bi也就是滑这一圈所需的时间,得到剩余时间还可以滑多少圈,然后再加上 i i i,就可以求出一共能滑多少圈啦!

代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;

long long n, t, x, b[maxn], s[maxn], ans = 0;

int main(){
	cin >> n >> t;
	for (int i = 1; i <= n; ++i){
		cin >> x >> b[i];
		s[i] = s[i - 1] + x + b[i];//前缀和
	}
	for (int i = 1; i <= n; ++i)
		if (t >= s[i])
			ans = max(ans, i + (t - s[i]) / b[i]);//滑到第i圈一共所需的时间
	cout << ans << endl;
	return 0;
}

T4-混乱的文本

题目描述

小爱正在使用一种文本编辑器输入文字。文本编辑器的工作机制如下:

若用户键入一个 [,则光标立即跳到文本的开头;
若用户键入一个 ],则光标立即跳到文本的末尾;
若用户键入任意字母,则在光标处插入该字母,且光标停留在新插入字母的后面。
给定一个字符序列,表示小爱敲击键盘录入的符号序列,请输出最后获得的文本。

输入格式

一个字符序列:表示键入的字符序列。

输出格式

一个字符序列:表示最后获得的文本。

数据范围

n n n 表示输入字符序列的长度

∘ \circ 30 % 30\% 30% 的数据, 1 ≤ n ≤ 1000 1\leq n \leq 1000 1n1000
∘ \circ 60 % 60\% 60% 的数据, 1 ≤ n ≤ 20 , 000 1\leq n \leq 20,000 1n20,000
∘ \circ 100 % 100\% 100% 的数据, 1 ≤ n ≤ 300 , 000 1\leq n \leq 300,000 1n300,000

样例输入

abc[xyz]efg

样例输出

xyzabcefg

分析

通过读题我们可以知道:如果读到的是[,那么后面跟着的字母就会插到前面。注意:不是读一个字母就插到前面,而是[后的所有字母都一起插到前面。如果读到的是],那就好办了,直接插到记录答案的字符串 a n s ans ans里就行了。哦,对了,如果第一个字符是字母的话,直接插到 a n s ans ans里就可以了。
那我们该怎么解决[的情况呢?我们可以定义一个字符串数组 s t st st s t n st_n stn记录第 n n n个[后的所有字母。因为每读到一个[就会插到前面,所以把 s t st st数组从 s t n st_n stn一直输到 s t 1 st_1 st1就行了。最后输出 a n s ans ans就OK了。

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

string s, ans = "", st[300010];
char js = ']';
int n = 0;

int main(){
	cin >> s;
	for (int i = 0; i < s.size(); ++i){
		if (s[i] == '['){
			js = '[';
			n++;
		}
		else if (s[i] == ']')
			js = ']';
		else{
			if (js == '[') st[n] += s[i];
			else ans += s[i];
		}
	}
	for (int i = n; i >= 1; --i)
		cout << st[i];
	cout << ans << endl;
	return 0;
}

T5-最大子阵和

题目描述

给定 n × n n \times n n×n 个整数组成一个方阵 a i , j a_{i,j} ai,j,请找一个 k × k k \times k k×k 的子方阵,使得子方阵内的数字之和达到最大,输出这个最大值。

输入格式

第一行:两个整数 n n n k k k
第二行到第 n + 1 n+1 n+1 行:每行 n n n 个整数表示 a i , j a_{i,j} ai,j

输出格式

单个整数:表示最大的 k × k k \times k k×k 的子方阵的数字之和。

数据范围

∘ \circ 30 % 30\% 30% 的数据, 1 ≤ k ≤ n ≤ 30 1\le k \le n \le 30 1kn30
∘ \circ 60 % 60\% 60% 的数据, 1 ≤ k ≤ n ≤ 300 1 \le k \le n \le 300 1kn300
∘ \circ 100 % 100\% 100% 的数据, 1 ≤ k ≤ n ≤ 2500 1 \le k \le n \le 2500 1kn2500
∘ \circ 0 ≤ a i , j ≤ 1 , 000 , 000 0 \le a_{i,j} \le 1,000,000 0ai,j1,000,000

样例输入

3 2
1 2 3
3 1 2
0 2 4

样例输出

9

样例说明

右下角最大

分析

这题呢我们需要用到前缀和。注意:这里并不能用一般的方法来求前缀和,因为这里读入的是二维数组。我们用二维数组 s i , j s_{i,j} si,j记录从左上角到第 i , j {i,j} i,j个数所有数的和。那么该怎么求 s i , j s_{i,j} si,j的值呢?
如图, s i , j = s i − 1 , j + s i , j − 1 − s i − 1 , j − 1 + t s_{i,j}=s_{i-1,j}+s_{i,j-1}-s_{i-1,j-1}+t si,j=si1,j+si,j1si1,j1+t。其中 t t t为当前读入的数,即图中由绿色、蓝色和红色框起来的小正方形。
求了前缀和,然后就是解决问题了。首先枚举子方阵右下角的坐标,然后再分别推出左上角、左下角和右上角的坐标(分别为 s i − k , j − k , s i , j − k , s i − k , j s_{i-k,j-k},s_{i,j-k},s_{i-k,j} sik,jk,si,jk,sik,j)。用 v v v记录当 i , j i,j i,j为右下角时子方阵的总和,即 s i , j = s i − k , j − k + s i , j − k + s i − k , j s_{i,j}=s_{i-k,j-k}+s_{i,j-k}+s_{i-k,j} si,j=sik,jk+si,jk+sik,j。然后用 a n s ans ans记录 a n s ans ans v v v之间的最大值,最后输出就完事儿了。

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

int n, k, t;
long long s[3000][3000], ans = 0;

int main(){
	ios::sync_with_stdio(false);
	cin >> n >> k;
	memset(s, 0, sizeof(s));
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= n; ++j){
			cin >> t;
			s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + t;
		}
	long long v = 0;
	for (int i = k; i <= n; ++i)
		for (int j = k; j <= n; ++j){
			v = s[i][j] - s[i - k][j] - s[i][j - k] + s[i - k][j - k];
			ans = max(ans, v);
		}
	cout << ans << endl;
	return 0;
}

对了:用 c i n cin cin语句读入会超时,需要改成 s c a n f scanf scanf读入或写一行取消缓冲区同步的代码。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值