上海市计算机学会2023年2月月赛丙组

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

T1 格式改写

题目描述
给定一个仅由拉丁字符组成字符序列,需要改写一些字符的大小写,使得序列全部变成大写或全部变成小写,请统计最少修改多少个字符才能完成这项任务。
输入格式
一个字符序列:保证仅由拉丁字符构成
输出格式
单个整数:表示最少修改次数
数据范围
设输入的字符数量为 n,则保证1≤n≤100,000
样例数据
输入:
TheQuickBrownFoxJumpsOverTheLazyDog
输出:
9
说明:
将大写改小写

因为仅由拉丁字符组成,最简单的思路就是枚举大小写字符数量然后进行比较输出。

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

string s;

int main(){
	cin>>s;
	int n=s.size(),CNT=0;
	for(int i=0;i<n;++i)
		if(s[i]>='A'&&s[i]<='Z')++CNT;
	int cnt=n-CNT;
	if(cnt>CNT)cout<<CNT<<endl;//如果小写字符多,则将所有大写改成小写
	else cout<<cnt<<endl;//否则将小写改为大写
	return 0;
}

T2 倍数统计

题目描述
给定整数 a,b 与正整数 c,求出在 a 到 b 之间(包含 a 与 b)有多少整数是 c 的倍数。
输入格式
第一行:两个整数 a 与 b;
第二行:单个正整数 c。
输出格式
单个整数:表示答案。
数据范围
−109≤a≤b≤109
1≤c≤109
样例数据
输入:
4 6
5
输出:
1

这题直接枚举肯定是不可以的了。一开始思路是(设较小数为a,较大数为b)从a向后枚举,一直找到c的倍数,b向前枚举一直找到c的倍数。最后除一下(虽然初测过了,但是仍会有超时风险,如:-999999999 999999999 1000000000)

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

int a,b,c; 

int main(){
	cin>>a>>b>>c;
	if(a>b)swap(a,b);
	while(a%c!=0)++a;
	while(b%c!=0)--b;
	cout<<(b-a)/c+1<<endl;
	return 0;
}

当然有更简单的方法,先考虑(b>a>0):
a到b之间c的倍数=1到b之间c的倍数数量-1到a-1之间c的倍数数量
不过明白这一点也就好办了,如果都是负数,可以把他们都当作正数来处理;如果一个正数一个负数,(设a<0,b>0)那么先算1到b,再算-1到a,也就是1到-a。

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

int main(){
	int a,b,c;
	cin>>a>>b>>c;
	if(a>0&&b>0)cout<<b/c-(a-1)/c<<endl;
	else if(a<0&&b<0)cout<<(0-a)/c-(0-b-1)/c<<endl;
	else cout<<(0-a)/c+1+b/c<<endl;
	return 0;
}

T3 区间的并

区间的并
内存限制: 256 Mb时间限制: 1000 ms
题目描述
给定一个数轴上的 nn 个闭区间,第 ii 个闭区间的两端点为[a_i,b_i],它们的并集可以表示为若干不相交的闭区间,请按照左端点从小到大的顺序输出这些区间的并集。
输入格式
第一行:单个整数 n;
第二行到第 n+1 行:每行两个整数 a_i 与 b_i 表示一个闭区间 [a_i,b_i]。
输出格式
若干行:表示输入区间的并集。每行两个整数,表示一个闭区间的两个端点,这些闭区间应该按照起点从小到大排序。
数据范围
对于 50% 的数据,1≤n≤104,0≤a_i≤b_i≤104
对于 100% 的数据,1≤n≤105,0≤a_i≤b_i≤109
样例数据
输入:
3
10 12
1 3
2 5
输出:
1 5
10 12

这题可以简单标记,但是有一个问题:[1,2] [3,4]该不该合并?

有一个方法:下标*2标记
但是我懒先不写了
有一个类似的题目,这题……数据小,但是还是可以学习学习。
可以进行判断区间合并(是O(n2)算法),可以通过sort排序将比较优化到O(n)。

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

int n,cnt;

struct nod{
	int a,b;
}q[100010];

void work(){
	int x=q[1].a,y=q[1].b;
	for(int i=2;i<=n;++i){
		if(q[i].a>y){
			cout<<x<<" "<<y<<endl;
			x=q[i].a,y=q[i].b;//就是一个单独闭区间
		}
		else{
			y=max(y,q[i].b);//如果可以合并
		}
	}
	cout<<x<<" "<<y<<endl;
}

bool cmp(nod q,nod p){
	return q.a<p.a;
}

int main(){
	cin>>n;
	for(int i=1;i<=n;++i)
		cin>>q[i].a>>q[i].b;
	sort(q+1,q+1+n,cmp);
	work();
	return 0;
}

T4 平分数字(一)

平分数字(一)
内存限制: 256 Mb时间限制: 1000 ms
题目描述
给定 n 个整数:a_1,a_2 ,⋯,a_n,请判定能否将它们分成两个部分(不得丢弃任何数字),每部分的数字之和一样大。
输入格式
第一行:单个整数 n;
第二行:n 个整数,表示 a_1,a_2,…,a_n。
输出格式
若能否平分,输出 Matched,否则输出 No
数据范围
对于 50% 的数据,1≤n≤18;
对于 100% 的数据,1≤n≤24;
−10,000,000≤a_i≤10,000,000
样例数据
输入:
4
1 2 3 4
输出:
Matched
说明:
1 + 4 = 2 + 3
输入:
3
2 2 2
输出:
No

这题上手,直接爆搜,时间复杂度O(2n)
最坏情况复杂度:16777216,不会超时,裸搜题目。但是,要注意:选择次序与结果无关!什么意思----先选择与后选择结果一样(否则复杂度就是O(nn))

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

int n,a[25],vh[25];
bool f=1;//找到一种可行方案就把它标成false退掉搜索

void dfs(int step,int s1,int s2){
	if(!f)return;
	if(step==n){
		if(s1==s2){
			cout<<"Matched"<<endl;
			f=0;
		}
		return;
	}
	for(int i=1;i<=25;++i){
		if(vh[i])continue;
		vh[i]=1;
		dfs(step+1,s1+a[i],s2);
		dfs(step+1,s1,s2+a[i]);
		vh[i]=0;
     break;
	}
}

signed main(){
	cin>>n;
	for(int i=1;i<=n;++i){
		cin>>a[i];
	}
	dfs(0,0,0);
	if(f)cout<<"No"<<endl;
	return 0;
}

T5 圆环三染色

圆环三染色
内存限制: 256 Mb时间限制: 1000 ms
题目描述
有一个圆环上有 n 个点,一个染色方案需要为每个点分配三种颜色中的一种,且圆环上相邻的点颜色不能相同。
请求出有多少种染色方案。答案可能很大,输出模 1,000,000,007 的余数。
输入格式
单个整数表示 n。
输出格式
表示方案数模 1,000,000,007 的余数。
数据范围
对于 30% 的数据,1≤n≤20;
对于 60% 的数据,1≤n≤1,000,000;
对于 100% 的数据,1≤n≤1018
样例数据
输入:
1
输出:
3
输入:
3
输出:
6

暴力就不写啦!!!
这题60%数据可以用递归递推解决。(需要以2,3为边界,以1为边界是错误的)
如果n>3:
先考虑第1,n-1,n个的颜色。
Firstly:1的颜色==第n-1的颜色,那么第n个可以有2种可能。那么1到n-1共有多少可能?----因为1的颜色=n-1的颜色,所以1的颜色!=n-2的颜色,所有共有f(n)种可能,再乘上第n个的可能,共有2*f(n-2)可能。
Secondly:1的颜色!=第n-1的颜色,那么第n个只有1种可能。1到n-1共有f(n-1)种可能,再乘上第n个的可能,共有f(n-1)的可能。
So,
f ( n ) = 2 ∗ f ( n − 2 ) + f ( n − 1 ) f(n) = 2*f(n-2)+f(n-1) f(n)=2f(n2)+f(n1)
可以过掉6个点。
记忆化递归:

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

int vh[1000010]={0,3,6,6};

int f(int n){
	if(vh[n])return vh[n];
	return vh[n]=(f(n-1)%mod+2*f(n-2)%mod)%mod;
}

signed main(){
	int n;
	cin>>n;
	cout<<f(n)<<endl;
	return 0;
}

递推(迭代):

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

int vh[10]={0,3,6,6};

signed main(){
	int n;
	cin>>n;
	int a=6,b=6;
	for(int i=4;i<=n;++i){
		int a1=b;
		b=(a*2+b)%mod;
		a=a1; 
	}
	if(n>3)cout<<b<<endl;
	else cout<<vh[n]<<endl;
	return 0;
}

递推(数组记录):

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

int vh[1000010]={0,3,6,6};

signed main(){
	int n;
	cin>>n;
	for(int i=4;i<=n;++i){
		vh[i]=(vh[i-1]+vh[i-2]*2)%mod;
	}
	cout<<vh[n]<<endl;
	return 0;
}

一开始分析问题是1,n-1,n的可能是8,那总共就是2n经过找规律和一系列蒙猜,发现如果是奇数+2,偶数再-2
这儿呢,还要用一个快速幂。

#include <bits/stdc++.h>
#define mod 1000000007
#define int long long//必须开long long否则会爆。
using namespace std;

int n;

int f(int step){
	if(step==1)return 2;
	int t=f(step/2);
	if(step%2==0)return t*t%mod;
	return t*t%mod*2%mod;
}

signed main(){
	cin>>n;
	if(n==1)cout<<3<<endl;
	else if(n==2)cout<<6<<endl;
	else if(n==3)cout<<6<<endl;
	else if(n%2==1)cout<<(f(n)-2+mod)%mod<<endl;//注意不要mod过再减,否则会有-1
	else cout<<(f(n)+2)%mod<<endl;
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

GaoGuohao2022

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

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

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

打赏作者

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

抵扣说明:

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

余额充值