SDU csp第二次模拟

A HRZ的序列

题目

相较于咕咕东,瑞神是个起早贪黑的好孩子,今天早上瑞神起得很早,刷B站时看到了一个序列aaa,他对这个序列产生了浓厚的兴趣。

他好奇是否存在一个数KKK,使得一些数加上KKK,一些数减去KKK,一些数不变,使得整个序列中所有的数相等。

其中对于序列中的每个位置上的数字,至多只能执行一次加运算或减运算或是对该位置不进行任何操作。

由于瑞神只会刷B站,所以他把这个问题交给了你!

输入

输入第一行是一个正整数 t t t表示数据组数。

接下来对于每组数据,输入的第一个正整数nnn表示序列aaa的长度,随后一行有nnn个整数,表示序列aaa

输出

输出共包含ttt行,每组数据输出一行。对于每组数据,如果存在这样的K,输出"YES",否则输出“NO”。(输出不包含引号)

样例

样例输入:

2
5
1 2 3 4 5
5
1 2 3 4 5

样例输出:

NO
NO

在这里插入图片描述

思路:

这个题目处的非常玄乎的感觉,但是经过分析就会发现,满足的情况要么不重合的数字数为1或2,要么是不重合的数字数是3,且这三个数等差。
因此就可以把所有的数存储进set里,自动去重,分析计算。
题目中有几个坑:
(1)根据数据范围可知用 long long 类型
(2)因为输入的是多组数据,每次使用的时候需要clear()
(3)因为数多组数据,中间判定得出的结果应该是continue,(千万不能return…)

代码:

#include<stdio.h>
#include<iostream>
#include<set>
#include<string.h>
using namespace std;
long long int a[10010];
long long int b[4];

int main()
{
	int t,n;
	cin>>t;
	set<long long int> s;
	while(t--)
	{
		cin>>n;
		for(int i=0;i<n;i++){
			cin>>a[i];
			s.insert(a[i]);
		}
		if(s.size()<3){
			printf("YES\n");
		}
		else if(s.size()==3){
			set<long long int>::iterator it;
			int i=0;
			for(it=s.begin();it!=s.end();it++){
				b[i]=*it;
				i++;
			}
			if(2*b[1]==b[0]+b[2]){
				printf("YES\n");
			}
			else printf("NO\n");
		}
		else {
			printf("NO\n");
		}
		memset(a,0,sizeof(a));
		memset(b,0,sizeof(b));
		s.clear();
	}
	return 0;
 } 

B: HRZ学英语

题目

瑞神今年大三了,他在寒假学会了英文的26个字母,所以他很兴奋!

于是他让他的朋友TT考考他,TT想到了一个考瑞神的好问题:给定一个字符串,从里面寻找 连续的26个大写字母 并输出!

但是转念一想,这样太便宜瑞神了,所以他加大了难度:现在给定一个字符串,字符串中包括26个大写字母和特殊字符’?’,特殊字符’?'可以代表任何一个大写字母。

现在TT问你是否存在一个 位置连续的且由26个大写字母组成的子串 ,在这个子串中每个字母出现且仅出现一次,如果存在,请输出从左侧算起的第一个出现的符合要求的子串,并且要求,如果有多组解同时符合位置最靠左,则输出字典序最小的那个解!如果不存在,输出-1!

这下HRZ蒙圈了,他刚学会26个字母,这对他来说太难了,所以他来求助你,请你帮他解决这个问题,报酬是可以帮你打守望先锋。

说明:字典序 先按照第一个字母,以 A、B、C……Z 的顺序排列;如果第一个字母一样,那么比较第二个、第三个乃至后面的字母。如果比到最后两个单词不一样长(比如,SIGH 和 SIGHT),那么把短者排在前。例如

AB??EFGHIJKLMNOPQRSTUVWXYZ
ABCDEFGHIJKLMNOPQRSTUVWXYZ
ABDCEFGHIJKLMNOPQRSTUVWXYZ

上面两种填法,都可以构成26个字母,但是我们要求字典序最小,只能取前者。

注意,题目要求的是 第一个出现的, 字典序最小的 !

输入

输入只有一行,一个符合题目描述的字符串。

输出

输出只有一行,如果存在这样的子串,请输出,否则输出-1

样例

样例输入1:

ABC??FGHIJK???OPQR?TUVWXY?

样例输出1:

ABCDEFGHIJKLMNOPQRSTUVWXYZ

样例输入2:

AABCDEFGHIJKLMNOPQRSTUVW??M

样例输出2:

-1

在这里插入图片描述

思路:

需要找到最早出现26个字母的排序,从前向后找得到的第一个满足条件的序列,输出即可。
遍历每一个字符,首先记录答案区间的左端点为0,有两种情况,问号和字母。因为问号能代替任意值,只计数即可。当出现字母的时候,需要用一个字母是否出现过的标记sign数组,用于标志字符出现几次,有两种情况,第一次出现和第二次出现。当第一次出现的时候,直接记录位置和数量;当字符出现的次数为2 的时候,就说明之前已经重复过一次,需要更新左端点。
假设出现字符数为2的字符是c,
更新的方法如下:将c 上次出现的位置(因此需要记录每一个字符上次出现的位置,用flag标记)的下一个作为答案区间的左端点,然后将这个左端点到现在位置的字符的sign 和flag 重新计数。
输出:此时有26个字符,包含问号和字母,字母是不重复的,并且要求字典序最小。因此此时将问号需要替换的字符记录在s 数组中,输出遇到问号的时候进行替换。

总结:

写的时候最主要的错误还是更新的时候考虑的不够全面。跟新很重要,很重要…

代码:

#include<stdio.h>
#include<iostream>
#include<string.h> 
using namespace std;
string a;
int sign[26];  //标记有没有
int flag[26];  //记录最后出现的位置 
int s[26];
int main()
{
	cin>>a;
	int start=0,i=0;
	int m=0;  
	for( int i=0;i<a.size();i++)
	{
			if(a[i]-'A'!=-2){ //非问号 
				sign[a[i]-'A']++; 
				if(sign[a[i]-'A']==1){
					flag[a[i]-'A']=i;  //当前出现的位置
				}
				if(sign[a[i]-'A']==2){  //已经有了 更新位置 
					memset(sign,0,sizeof(sign));  //当前不可能
					for(int j=flag[a[i]-'A']+1;j<=i;j++){
						sign[a[j]-'A']++;
					}
					start=flag[a[i]-'A']+1 ; //下一个    //当不满足条件时的起始位置和相应的变化 
					flag[a[i]-'A']=i;
				}
			}
			if(i-start==25)
			{
					//满足一个,中间可能有问号  sign<=1
				int t=0;
				for(int k=0;k<26;k++){
					if(sign[k]==0){
						s[t]=k;   //记录问号的数量 和问号处应该的字母 
						t++;
					}
				}
				t=0;
				for( int j=start;j<=i;j++){
					if(a[j]!='?'){
						printf("%c",a[j]);
					}
					else{
						char c=s[t]+'A';
						printf("%c",c);
						t++;
					}
				}
				printf("\n");
				return 0;
			}	
	} 
	printf("-1\n");
	return 0; 
}

C:咕咕东的奇妙序列

题目:

咕咕东 正在上可怕的复变函数,但对于稳拿A Plus的 咕咕东 来说,她早已不再听课。

此时她在睡梦中突然想到了一个奇怪的无限序列:112123123412345…

这个序列由连续正整数组成的若干部分构成,其中第一部分包含1至1之间的所有数字,第二部分包含1至2之间的所有数字,第三部分包含1至3之间的所有数字,第i部分总是包含1至i之间的所有数字。

所以,这个序列的前56项会是11212312341234512345612345671234567812345678912345678910,其中第1项是1,第3项是2,第20项是5,第38项是2,第56项是0。

咕咕东 现在想知道第 k 项数字是多少!但是她睡醒之后发现老师讲的东西已经听不懂了,因此她把这个任务交给了你。

输入:

输入由多行组成。
第一行一个整数q表示有q组询问(1<=q<=500)(1<=q<=500)(1<=q<=500)
接下来第 i+1行表示第i个输入 ki ,表示询问第 ki项数字。(1<=ki<=1018) (1<=ki<=10^18)

输出:

输出包含 q 行

第i行输出对询问 ki 的输出结果。

样例:

样例输入:

5
1
3
20
38
56

样例输出:

1
2
5
2
0

思路:

这道题给人的感觉就是纷繁复杂,层层叠叠。
暴力,看看数据范围,别想了…
以下尝试分成下面的几个层
(1)位数段 一位数(1)、二位数(2)、三位数(3)
(2)n 位数 段
1.
1 2
1 2 3
1 2 3…n
(3) 段内,(终于这步可以稍微连续点了)
1 2 3 4 …7 8 9 1 0 1 1 … 1 0 0 1 0 1
现在的问题就变成了,从这三层一步一步的查找了,最外层二分查找是那个大段的,然后根据数量确定是到几(第二层)比如是 150 内的,然后在 150 内部查找。
注意可以提前返回的条件,比如正好是上一段的末尾。

代码:

#include<stdio.h>
using namespace std;

long long getZ(long long x)
{
	long long d=0,n=0,a0=1,m=1,ans=0;//d是公差,m是每一段的结尾项-
	while(1)       //1-9 1,  12多两位      
	{
		m*=10;           //m-1是段的结尾组值    m=10  m=100 
		n=m-m/10;       //n是每段的组数         n=9   n=90
		d++;            //当前段下的位数       d=1    d=2
		if(x<=m-1)      //x组在本段内    m-1  9   99
		{
			n=x-(m/10-1);  // n=x-9=4    10 11 12 13
			ans+=n*a0+n*(n-1)*d/2;//sum是前x组总共的位数  xd=  
			break; 
		}
		else   //x组在之后的段里   //计算之前组的位数 
		{
			ans+=n*a0+n*(n-1)*d/2;   
			a0=a0+n*d+1; 
		} 
	}
	return ans; 
}

int main()
{
	int q;
	scanf("%d",&q);
	while(q--)
    {
    	long long k;
    	scanf("%lld",&k);
		long long l=0,r=1e9,mid=0;
		long long ans=0; 
		while(l<=r)
		{
			mid=(l+r)>>1;
			long long t=getZ(mid); 
			if(k>t)
			{
				ans=mid;
				l=mid+1; 
			}
			else
			{
				r=mid-1;
            }
		} 
    	long long s=k-getZ(ans);
    	int e; 
		if(s==0)
		{
			e=ans%10;
			printf("%d\n",e);
	        continue;
		} 
		long long m,n,d,sum,d1;
        m=1;
		n=d=sum=d1=0;	
	 	while(s) 
		{
			m*=10;
			n=m-m/10;
			d++;
			if(s>n*d){
			    sum+=n;
			    s=s-n*d;
			}
			else {
				sum+=s/d; 
				d1=s%d;
				break; 
			} 
		} 
		if(d1!=0){
			sum++;
			while(d!=d1){
				sum=sum/10;
				d=d-1;
			}
		}
		e=sum%10;
		printf("%d\n",e);
	}
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值