上海市计算机学会竞赛平台2023年6月月赛丙组题目解题报告

上海市计算机学会竞赛平台2023年6月月赛丙组题目解题报告

T1-差分求和

题目描述

给定一个整数序列,对其中任意两个数,计算后项减前项的差,输出这些差的差。
例如对于 a 1 , a 2 , a 3 , a 4 a_1,a_2,a_3,a_4 a1,a2,a3,a4 来说,输出
( a 2 − a 1 ) − ( a 3 − a 1 ) − ( a 3 − a 2 ) − ( a 4 − a 1 ) − ( a 4 − a 2 ) − ( a 4 − a 3 ) (a_2-a_1)-(a_3-a_1)-(a_3-a_2)-(a_4-a_1)-(a_4-a_2)-(a_4-a_3) (a2a1)(a3a1)(a3a2)(a4a1)(a4a2)(a4a3)

输入格式

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

输出格式

单个整数表示结果

数据范围

50 % 50\% 50% 的数据, 1 ≤ n ≤ 500 , 1 ≤ a i ≤ 500 1≤n≤500,1≤a_i≤500 1n5001ai500
50 % 50\% 50% 的数据, 1 ≤ n ≤ 500 , 000 , 1 ≤ a i ≤ 500 , 000 1≤n≤500,000,1≤a_i≤500,000 1n500,0001ai500,000

样例数据

输入:

3
1 2 3 

输出:

-2
分析1:暴推yyds!!!

对一些刚学过for循环的正好实用,但缺点就是两层嵌套,只有50分

代码1:50分

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

long long n,a[500000];

int main(){
	cin>>n;
	for(int i=1;i<=n;++i){
		cin>>a[i];
	}
	long long ans=a[2]-a[1];//因为第一项总是(a2-a1),所以先算了
	for(int i=3;i<=n;++i){//从第三个开始,不要担心n小于三,因为小于三就不执行了
		for(int j=1;j<i;++j){
			ans-=a[i]-a[j];//相减
		}
	}
	cout<<ans << endl;
	return 0;
}
分析2:偷袭!!!

O ( n 2 ) O(n^2) O(n2)效率太低了,只能有五十分,那只能采取偷袭的方法,出其不意!!!

用1,2,3三个数距离

根据上面的公式是:

( 2 − 1 ) − ( 3 − 1 ) − ( 3 − 2 ) = − 2 (2-1)-(3-1)-(3-2)=-2 (21)(31)(32)=2

把公式拆开后:

2 − 1 − 3 + 1 − 3 + 2 2-1-3+1-3+2 213+13+2

简单排一下后:

2 − 1 + 1 + 2 − 3 − 3 2-1+1+2-3-3 21+1+233

看到了吗,除了原来的 2 − 1 2-1 21,还要加一下从二开始每个点的前缀和,然后再减去从三开始的每个数去乘当前i-1

可能有些同学认为是巧合,我们用题目开头的那个例子来实验:

原式:
( a 2 − a 1 ) − ( a 3 − a 1 ) − ( a 3 − a 2 ) − ( a 4 − a 1 ) − ( a 4 − a 2 ) − ( a 4 − a 3 ) (a_2-a_1)-(a_3-a_1)-(a_3-a_2)-(a_4-a_1)-(a_4-a_2)-(a_4-a_3) (a2a1)(a3a1)(a3a2)(a4a1)(a4a2)(a4a3)

拆括号和排序:
a 2 − a 1 + a 1 + a 2 + a 1 + a 2 + a 3 − a 3 − a 3 − a 4 − a 4 − a 4 a_2-a_1+a_1+a_2+a_1+a_2+a_3-a_3-a_3-a_4-a_4-a_4 a2a1+a1+a2+a1+a2+a3a3a3a4a4a4
看到了吗,加上从二开始的每个点的前缀和,再减去从三开始的每个点成当前i减1。

代码2:100分

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

long long n,a[500010],vh[500010];

int main(){
	cin>>n;
	for(int i=1;i<=n;++i){
		cin>>a[i];
		vh[i]=vh[i-1]+a[i];
	}
	long long ans=a[2]-a[1];
	for(int i=2;i<n;++i) ans+=vh[i];
	for(int i=3;i<=n;++i) ans-=a[i]*(i-1);
	cout<< ans << endl;
    return 0;
}

T2-二进制异或

题目描述

给定两个只包含 0,1 的二进制数字,请你求出两个数字异或以后的二进制值是多少?(两个数字异或是指其二进制形式下按位做异或运算)

所谓异或,是指不同值异或结果为1,相同值异或结果为0,即:
0 ⊕ 0 = 0 0⊕0=0 00=0
0 ⊕ 1 = 1 0⊕1=1 01=1
1 ⊕ 0 = 1 1⊕0=1 10=1
1 ⊕ 1 = 0 1⊕1=0 11=0

输入格式

输入共两行,每行一个二进制数字

输出格式

输出共一行,一个二进制数字表示答案

数据范围

对于 50 % 50 \% 50%的数据,输入的二进制数字长度不超过7位
对于 100 % 100 \% 100%的数据,输入的二进制数字长度不超过100位

注意:输入的两个二进制数字长度可能不相同

样例数据

输入1:
1101
1110
输出1:
11
说明1:
按位异或的答案为0011,去掉前导0后即为11

输入2:
100101
1010
输出2:
101111

分析

这题是要我们求两个二进制数异或后的值。首先我们需要知道异或是怎么算的。题目中已经给出:相同为0,不同为1。因此我们可以用两个字符串 s 1 s_1 s1 s 2 s_2 s2 来表示两个二进制数, 然后用第三个字符串 s s s 记录最终异或的答案。注意:输入的两个二进制数字长度可能不相同!此时应该在较短的数前面补0。同时,异或时要从数的末尾开始一一匹配,然后进行运算,不是从头开始的!!! 最后,因为有前导0的存在,我们需要写一个删除前导0的while语句,最后输出 ,完事儿,下班!
OK!上代码!

#include <bits/stdc++.h>
#define int long long
using namespace std;

string s1, s2, s;

signed main(){
	cin >> s1 >> s2;
	int l1 = s1.size(), l2 = s2.size();
	if (l1 > l2){//帮短的数补0使它变长
		for (int i = 1; i <= l1 - l2; ++i)
			s2 = '0' + s2;
		l2 = l1;
	}
	else if (l1 < l2){
		for (int i = 1; i <= l2 - l1; ++i)
			s1 = '0' + s1;
		l1 = l2;
	}
	for (int i = 0; i < l1; ++i){//打工仔异或工作中
		if (s1[i] == s2[i])
			s += '0';
		else
			s += '1';
	}
	int l = s.size();
	while (s[0] == '0'){//删除影响我们AC的前导0
		for (int i = 0; i < l - 1; ++i)
			s[i] = s[i + 1];
		l--;
	}
	if (s == '')//判断一下s是否是空的
		cout << 0 << endl;
	else {//正常情况,正常输出,完美!
		for (int i = 0; i < l; ++i)
			cout << s[i];
		cout << endl;
	}
	return 0;
}

等等,还没完呢!如果 s s s 里面都是0该咋办?一删不就空了吗!还咋输出啊?!此时应该请特判同学出场······如果 s s s 是空的,那么就输出0。(值得一提的是,我在前一个程序上并没有写特判,但我还是AC了,所以说,这次数据是真的良心)

T3-颜文字

内存限制: 256 Mb
时间限制: 1000 ms

题目描述

聊天对话中会用一些符号表示心情。:-) 表示好心情,:-( 表示坏心情。给定一串字符序列表示用户的聊天记录,用户的心情指数定义为其中好心情数量减去坏心情数量。

请统计并输出用户的心情指数。

输入格式
  • 一串字符表示聊天记录
输出格式
  • 单个整数表示用户的心情指数
数据范围

n n n 表示输入序列的单词数量,则

  • 1 ≤ n ≤ 10000 1≤n≤10000 1n10000

这道题应该用一个while循环,以此判断是否有对话读入。每次读入一个字符串,用cin,读入到空格自动终止,如果是:-),则开心指数+1,反之,坏心情+1,随后输出相减结果。

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

int h=0,s=0;
string st;

int main(){
	while(cin>>st){
		if(st==":-)") h++;
		if (st==":-(") s++;
	}
	cout<<h-s<<endl;
	return 0;
}

T4-选取子段(二)

题意

给定一个长n的序列,从中截取一个任意长的字段,确保其间每个元素相同,问一共能截取多少这样的字段。

分析

首先,所有子段都是连续的,所以可以先将字符串分割成元素相同的几段。
然后,计算每个子段可以分成几段,最后累加起来输出
假设有一个长m的字段:
1、分成一个一组,一共分成n组。
2、分成两个一组,一共分成(n - 1)组
……
n、分成n个一组,一共分成1组
共: n + ( n − 1 ) + ( n − 2 ) + … … + 2 + 1 = ( 1 + n ) × n ÷ 2 n+(n-1)+(n-2)+……+2+1 = (1+n) \times n \div 2 n+(n1)+(n2)+……+2+1=1+n)×n÷2

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

long long n, a[MAXN], b[MAXN], num = 1;

int main(){
	cin >> n;
	cin >> a[1];
	b[1]++;
	for (int i = 2; i <= n; i++){
		cin >> a[i];
		if (a[i] == a[i - 1])//判断是否与上一个元素相同
			b[num]++;
		else{
		//否则新开一组记录
			num++;
			b[num]++;
		}
	}
	long long ans = 0;
	for (int i = 1; i <= num; i++){
		ans += (1 + b[i]) * b[i] / 2;//计算组数
	}
	cout << ans << endl;
	return 0;
}

T5-观光单车

题目描述

已知公园内有出借观光单车,每辆单车限坐两人,且两人体重之和不能超过观光单车的限重 T T T

小爱带领班级 n n n 位同学共同前往借车浏览风景,已知每位同学的体重分别为 w 1 , w 2 , , . . . , w n w_1,w_2, ,...,w_n w1,w2,,...,wn ,请问该班级最少借多少辆车,才能满足每个人的乘坐需求。

输入格式
输入共两行:

第一行,两个正整数 n , T n,T n,T
第二行, n n n个正整数 w 1 , w 2 , , . . . , w n w_1,w_2, ,...,w_n w1,w2,,...,wn

输出格式

输出一个正整数,表示最少租借单车的数量。

数据范围

对于 30 % 30\% 30% 的数据, 1 ≤ n ≤ 10 1≤n≤10 1n10

对于 60 % 60\% 60% 的数据, 1 ≤ n ≤ 1 0 3 1≤n≤10^3 1n103

对于 100 100% 100 的数据, 1 ≤ n ≤ 1 0 5 , 1 ≤ w i ≤ T ≤ 1 0 4 1≤n≤10^5 ,1≤w_i≤T≤10^4 1n105,1wiT104

样例数据

输入:

7 50
15 41 32 42 27 25 19

输出:

5

分析:这题可以用贪心来写,首先先排完序,然后第一与最后比较,如果小于T,那就说明可以做一组,右指针往左一位,左指针往右一位。如果大于了,你想想,左面的数越往后指,数越大,小的都大了,大的怎么比,说明右面只能单独做一辆车,右指针单独往左移

代码:100分

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

long long n,t,a[100010],ans=0;

int main(){
	cin>>n>>t;
	for(int i=1;i<=n;++i) cin>>a[i];
	stable_sort(a+1,a+n+1);
	long long l=1,r=n;
	while(l<=r){
		if(a[l]+a[r]<=t){//小于时
			ans++,l++,r--;//一起移
		}else{
			ans++,r--;//当不一样时,大的单独一辆,右指针单独往左移
		}
	}
	cout<< ans << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GaoGuohao2022

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值