CSP-J复赛模拟赛五

1.重复判断

题目描述

小可需要判断字符串a是否是字符串b重复若干次得到的。输入一个数字 t,表示共有 t 组询问。接下来 t 行每行两个字符串 a 和 b。输出 t 行,每行输出 YESNO。

思考

如果a不是字符串b重复若干次得到的,长度相除的余数不会是零(不能整除),输出NO。便利字符串a,每次将a中长度与b长度相等的子串和b进行比较,一次不相等,则输出NO,否则输出YES。

思路

和比赛中的思考相同。

AC代码

#include<iostream>
#include<string>
using namespace std;
int main(){
//	freopen("repeat.in","r",stdin);
//	freopen("repeat.out","w",stdout);
	int t;
	string a,b;
	cin>>t;
	while(t--){
		cin>>a>>b;
		int f=0,l=0,len=b.size();
		if(a.size()%len!=0){
			cout<<"NO"<<endl;
			continue;
		}
		while(l+len<=a.size()){
				if(a.substr(l,len)!=b){
					f=1;
					break;
				}
			l+=len;
		}
		if(f==1) cout<<"NO"<<endl;
		else cout<<"YES"<<endl;
	}
//	fclose(stdin);
//	fclose(stdout);
	return 0;
}

2.歪果仁学乘法

题目描述

对于a × b:(1<=a,b<=99)

1.将a,b的每一位上的数码画成线,不同位之间分隔开。
2.a 和 b 的方向垂直画出。
3.数出每个方向上交点的个数,即是 c 对应位置上的数码。

请给出两个数字 a, b,求它们的乘积时交点的总个数是多少。

计算12 ×13的方法:

1.红色线分别画出 1 条和 2 条;
2.蓝色线分别画出 1 条和 3 条;
3.数出红、蓝色线的交点个数,依次为 1,5,6 个;

思考

a,b是两位数, 只要把a的十位数和b的十位数、a的个位数和b的十位数、只要把a的十位数和b的个位数、a的个位数和b的个位数 的乘积相加就知道交点的总个数了。

思路

和比赛中的思考相同。

AC代码

#include<iostream>
using namespace std;
int main(){
//	freopen("multiplication.in","r",stdin);
//	freopen("multiplication.out","w",stdout);
	int a,b,sum=0;
	cin>>a>>b;
	cout<<(a/10)*(b%10)+(a/10)*(b/10)+(a%10)*(b%10)+(a%10)*(b/10);
//	fclose(stdin);
//	fclose(stdout);
	return 0;
}

3.去重求和

题目描述

小可有一个长度为 n 的序列 a​i​​。他定义sum(l,r),为a[l]~a[r] ,这些数去重之后的和,请求出

输入一个正整数 n ,表示序列长度。输入n个整数,表示序列 a ,答案对10^9+710​9​​+7取模。(1≤n≤5∗10^​5​​,1≤a​i​​≤10^​9​​)

思考

使用暴力解法,遍历a数组从l到r,用map去重。循环遍历map,如果是1,a数组从l到r的加和就加等于k,最后算出所有加和的总和。

思路

a1,a2,a3,a4,a5......,an

a1               a2出现次数=a1出现次数-1;
a1 a2
a1 a2 a3
a1 a2 a3 a4

...............

a1 a2 . . . . . . .an    ai出现次数=aj出现次数-(i-j);

a[1]~a[n]中a1重复计算n次;a[1]~a[n]中a2重复计算n-1次,a[2]~a[n]中重复计算n-1次,

可以发现a[1]计算了(n-1+1)*1次,a[2]计算了(n-2+1)*2次,那么a[i]计算了(n-i+1)*i次。

而在去重时a[i]重复会比第一次出现少算第一次出现的位置次,所以可以将map[a[i]]赋为0,表示还未出现,出现第一次标记下标。

AC代码

#include<iostream>
#include<map>
using namespace std;
const long long mod=1e9+7;
long long n,a[500005],pos=0;
map<long long,long long> mp;
long long ans=0;
int main(){
//	freopen("summation.in","r",stdin);
//	freopen("summation.out","w",stdout);
	cin>>n;
	for(int i=1;i<=n;i++){cin>>a[i];mp[a[i]]=0;}
	for(int i=1;i<=n;i++){
	    ans=(ans+((i-mp[a[i]])*(n-i+1))%mod*a[i])%mod;
	    mp[a[i]]=i;
	}
	cout<<ans;
//	fclose(stdin);
//	fclose(stdout);
	return 0;
}

4.点集操作

题目描述

对于任选不同的两个点i,j,设 B 为一个最大的点集,满足 B 既是 A​i​​ 的子集,又是A​j​​的子集 。将 B 在图中变成一个新点,B 内的所有边全部删除。点集 B 以外的点与点集 B 以内的点的连边关系转移到新点上。

思考

给x到y加边,用dfs搜索i,j可以到达的点,便利找出最大相同子集,把与最大相同子集连接的点重新连到一个新点上,重复执行,最后输出剩余点数。

思路

邻接表或邻接矩阵存储图,图是无环的,所有我们知道只有出度的点能到达的点一定比有入度的点多,因此只要取只有出度的点为i,下一个有入度的点为j就能保证子集最大,剩余点数最小。

AC代码

#include<iostream>
#include<vector>
using namespace std;
int n,m,ans;
vector<vector<int> > v;
vector<int> vis,ind;
int main(){
//	freopen("point.in","r",stdin);
//	freopen("point.out","w",stdout);
	cin>>n>>m;
	v.resize(n+1);
	vis.resize(n+1);
	ind.resize(n+1);
	while(m--){
		int x,y;
		cin>>x>>y;
		v[x].push_back(y);
		++ind[y];
	}
	for(int i=1;i<=n;i++){
		if(ind[i]){
			for(int j=0;j<=v[i].size();j++){
				vis[a[i][j]]=1;
			}
		}
	}
	for(int i=1;i<=n;i++){
		if(vis[i]==0){
			ans++;
		}
	}
	cout<<ans;
//	fclose(stdin);
//	fclose(stdout);
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值