三道csp模拟题

题目一

在这里插入图片描述

分析

这道题并不会很难,只要稍微分析,就能发现,如果数字序列中只有1个或2个不同的数,就一定满足;有4个不同的数,就一定不满足;
唯一需要细加判断的就是3个不同的数,如果是等差数列(a[i+1]-a[i]==a[i]-a[i-1]),就满足条件。

所以步骤是
1、先为数组排序
2、用一个数组记录相互不同的数(因为>3的情况可以直接判定,那么只需要最多纪录3个数)
3、不同的数是3的情况,判断b[2]-b[1]==b[1]-b[0]是否成立。

总结

在这里,后面五个测试点是long long ,虽然俺注意到了,但是我用来记录数据的p忘记使用long long了,我的50分~~

代码

#include<stdio.h>
#include<algorithm>
using namespace std;
long long int a[10005];
int main()
{
	long long int t,b[5],n;
	scanf("%lld",&t);
	for(int i=0;i<t;i++)
	{
		scanf("%lld",&n);
		for(int j=0;j<n;j++)//序列个数
			scanf("%lld",&a[j]);
		sort(a,a+n);
		long long int p=a[0];//用来记录
		b[0]=p;int k=1,j=1;
		while(j!=n)
		{
			if(p!=a[j]){
				p=a[j];b[k]=p;k++;
			}
			if(k>3)
			{
				printf("NO\n");
				break;
			}
			j++;
		}
		if(k==1||k==2)
			printf("YES\n");
		//k==3的情况
		if(k==3)
			if((b[1]-b[0]==b[2]-b[1]))
				printf("YES\n");
			else
				printf("NO\n");
	}
	return 0;
}

题目二

在这里插入图片描述

分析

这道题使用尺取法,l记录符合条件的左端,r记录符合条件的右端;
每次r++的时候,判断visit[r]是否>1(也就是在这个字符段里面有重复的字母,一定不符合条件);
如果>1,那么就要将l的值移动到r所访问的字母的前面相同字母的下一个。
如果r-l+1==26,那么就找到了符合条件的字符段;
最后需要处理的是?,将缺失的字母整合起来排序,只要遇到?就用字典序最小的字母代替。

总结

在使用尺取法的时候,移动l的时候没有注意到,是移动到r所访问的字母前面与之相同字母的下一个,我直接将l移动到r前面那个字母那里了。
所以我竟然还有40分,不可思议。

代码

#include<stdio.h>
#include<string.h>
#include<map>
using namespace std;

map<char,int> visit;
void ini(){
	visit['A']=0;visit['B']=0;visit['C']=0;visit['D']=0;visit['E']=0;visit['F']=0;
	visit['G']=0;visit['H']=0;visit['I']=0;visit['J']=0;visit['K']=0;visit['L']=0;
	visit['M']=0;visit['N']=0;visit['O']=0;visit['P']=0;visit['Q']=0;visit['R']=0;
	visit['S']=0;visit['T']=0;visit['U']=0;visit['V']=0;visit['W']=0;visit['X']=0;
	visit['Y']=0;visit['Z']=0;visit['?']=0;
}
int main()
{
	char c[10000005];
	scanf("%s",c);
	int len=strlen(c);
	ini();
	int l=0,r=0,flag=0;
	while(l<=r&&r<=len)
	{
		visit[c[r]]++;
		if(visit[c[r]]>1&&c[r]!='?'){//有重复的,这26个字母不可能
			while(l<r){
				if(c[l]==c[r]) break;
				visit[c[l]]--;
				l++;
			}
			visit[c[l]]--;l++;
		}
		if(r-l+1==26)//符合条件
		{
			flag=1;break;
		}
		r++;
	}
	if(flag==0) printf("-1\n");
	else
	{//把?替换掉
		char queshao[26];
		int k=0;
		//寻找缺少哪些字母
		for(int i=0;i<26;i++)
		{
			if(visit['A'+i]==0){
				queshao[k]='A'+i;k++;
			}
		}
		k=0;
		for(int i=l;i<=r;i++)
		{
			if(c[i]!='?') printf("%c",c[i]);
			else
			{
				printf("%c",queshao[k]);k++;
			}
		}
		printf("\n");
	}
	return 0;
}

题目三

在这里插入图片描述

分析

这是一道我很想暴力解决的问题,可是暴力过不了~
但是从暴力的方法中多少能借鉴点东西;
具体的想法就是,先找到x所在的组n;然后x-x前面的n-1组的和,从而更新x;然后根据x找到它在组n当中的第几个数,最后寻找x在这个数当中是第几个(位数)。
首先我暴力的求出前n-1组的和,但是好像复杂度很高;然后看别人采用两次二分去降低复杂度,就用两次二分了。

既然用到了二分的思想,就从组当中最后一个数x入手,计算该组以及前面组的总位数

将1-9;10-99;100-999…分为同一簇(同方差)
比如说x=102;那么就是在第102组当中,计算前102组的位数总和就分为两个部分,一是占用了整个簇的部分:1-9和10-99(计算前99组可以用等差数列的前n项和计算);二是占用部分簇的部分:100-102(项数=102-100+1,还是用前n项和实现)
使用二分就能不断逼向最符合的组,从而得到在哪个组。

总结

代码

#include<stdio.h>
using namespace std;
int ans[1000];
long long sum1(long long x,int flag)
{
	long long int sum=0,w=0;
	long long int n=0,a1=0,an=0,d=0;
	while(1)
	{
		if(w==0)	w=1;
		else 	w=w*10;
		if(x>w*10-1){
			n=10*w-w;d++;a1=an+d;an=a1+(n-1)*d;sum=sum+(a1+an)*n/2;
		}
		else{
			n=x-w+1;d++;a1=an+d;an=a1+(n-1)*d;sum=sum+(a1+an)*n/2;
			break;
		}
	}
	if(flag==1)	return sum;
	else 	return an;
}

void solve(long long x)
{
	long long int p1 = 0, p2 = 0;
	//erfen1
	long long l=0, r=1000000000;
	while(l<r-1){
		long long mid = (l+r)>>1;
		if(sum1(mid,1) < x){
			l = mid;
		}
		else{
			r = mid;
			p1 = mid;//p1是要找的组数
		}
	}
	x=x-sum1(p1-1,1);//在该组的第几个数
	//二分找是数字几
	l=0, r=p1+1;
	while(l<r-1){
		long long mid = (l+r)>>1;
		if(sum2(mid,0) < x){
			l = mid;
		}
		else{
		p2 = mid;
		r = mid;
		}
	}
	x=x-sum2(p2-1,0);//该数字的第几个
	//printf("p2: %d x:%d \n",p2,x);
	/*
	//第几位
	int t=1,j=1;
	while(t!=0){
		t=(p2)/pow(10,j);j++;
	}//j-1位数
	int ans=((p2)%(long long int)pow(10,j-x))/pow(10,j-1-x);*/
      int tot=0;
	while(p2){
		ans[tot] = p2%10;tot++;p2=p2/10;
	}
	printf("%d\n",ans[tot-x]);
}

int main(){
	long long int q,k;
	scanf("%lld",&q);
	for(int i=0;i<q;i++){
		scanf("%lld",&k);//第k个数
		solve(k);
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值