上海市计算机学会竞赛平台2023年6月月赛题解

第一题 差分求和

题目描述

给定一个整数序列,对其中任意两个数,计算后项减前项的差,输出这些差的差。
例如对于 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;
}

第二题 二进制异或

题目描述

给定两个只包含 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位
注意:输入的两个二进制数字长度可能不相同

样例数据

输入:

1101
1110

输出:

11

说明:

按位异或的答案为0011,去掉前导0后即为11

输入:

100101
1010

输出:

101111

分析:模拟

这题没啥好讲的,就是要注意在长短不一的情况下,要在短的前面补0,直到长度一致

代码:100分

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

string a,b;
long long ans[110],k=0;

int main(){
	cin>>a>>b;
	if(a.size()>b.size()){//考试时为了稳妥使用的笨方法
		long long f=a.size()-b.size();//a长度长时的解法
		for(int i=0;i<f;++i){
			ans[++k]=a[i]-'0';
		}
		for(int i=0;i<b.size();++i){
			if(a[i+f]==b[i]){
				ans[++k]=0;
			}else{
				ans[++k]=1;
			}
		}
	}else if(a.size()<b.size()){//b长度长时的解法
		long long f=b.size()-a.size();
		for(int i=0;i<f;++i){
			ans[++k]=b[i]-'0';
		}
		for(int i=0;i<a.size();++i){
			if(b[i+f]==a[i]){
				ans[++k]=0;
			}else{
				ans[++k]=1;
			}
		}
	}else{//相同时的解法
		for(int i=0;i<a.size();++i){
			if(b[i]==a[i]){
				ans[++k]=0;
			}else{
				ans[++k]=1;
			}
		}
	}
	bool f=true;
	for(int i=1;i<=k;++i){
		if(ans[i]==0){
			if(f==false){
				cout<< ans[i];
			}
		}else{
			cout<< ans[i];
			f=false;
		}
	}
	return 0;
}

第三题 颜文字

题目描述

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

输入格式

一串字符表示聊天记录

输出格式

单个整数表示用户的心情指数

数据范围

n n n 表示输入序列的单词数量,则 1 ≤ n ≤ 10000 1≤n≤10000 1n10000

样例数据

输入:

are you ok? :-)
i am ok :-)
no :-( bad 

输出:

1

分析:特性,哦不,机制解题

在C++中,有一个用法,叫:while(cin>>) 他有啥用呢?它是一段一段读的读到空格为一段,前面的颜文字前正好有个空格!!

代码:100分

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

string s;
long long x,y;

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

第四题 选取子段(二)

题目描述

给定一个长度为 n n n的序列 a 1 , a 2 , . . . , a n a_1 ,a_2 ,...,a_n a1,a2,...,an ,请问多少种方案,能够从中选取一个连续段,使得该子段内所有元素的值都相同?

输入格式

输入共两行:
输入第一行,一个正整数 n n n
输入第二行, n n n个整数 a 1 , a 2 , . . . , a n a_1 ,a_2 ,...,a_n a1,a2,...,an

输出格式

输出一个整数,表示方案数。

数据范围

对于 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 0 9 ≤ a i ≤ 1 0 9 1≤n≤10^5 , −10^9≤a_i ≤10 ^9 1n105,109ai109

样例数据

输入:

5
3 1 1 2 5

输出:

6

说明:

每个元素单独成段均可,共{3},{1},{1},{2},{5}这5种方案
第2、3个元素组成的子段{1,1}也满足要求。
所以共6种方案

分析:这题没啥好讲的,主要就是要会怎么算一个连续相同的区间里有多少个符合要求的,有一个公式就是:个数*(个数-1)/2

还有就是要注意最后也要算一下,因为如果整个数列都相同的话,最后还没有算就退循环了,所以最后要算一下

代码:100分

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

long long n,a[100010],ans=0,cnt=1;

int main(){
	cin>>n;
	for(int i=1;i<=n;++i){
		cin>>a[i];
	}
	long long f=a[1];
	for(int i=2;i<=n;++i){
		if(a[i]==f){
			cnt++;
		}else{
			ans+=cnt*(cnt-1)/2;
			cnt=1;
			f=a[i];
		}
	}
	ans+=cnt*(cnt-1)/2;
	cout<< ans+n << endl;//因为每个单独的都可以作为一个,所以直接加n就行了
	return 0;
}

第五题 观光单车

题目描述

已知公园内有出借观光单车,每辆单车限坐两人,且两人体重之和不能超过观光单车的限重 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
发出的红包

打赏作者

lyoi20210204

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

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

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

打赏作者

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

抵扣说明:

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

余额充值