Week 8 CSP M2

T1 HRZ的序列

问题描述

相较于咕咕东,瑞神是个起早贪黑的好孩子,今天早上瑞神起得很早,刷B站时看到了一个序列aa,他对这个序列产生了浓厚的兴趣。
他好奇是否存在一个数KK,使得一些数加上KK,一些数减去KK,一些数不变,使得整个序列中所有的数相等。
其中对于序列中的每个位置上的数字,至多只能执行一次加运算或减运算或是对该位置不进行任何操作。
由于瑞神只会刷B站,所以他把这个问题交给了你!

输入格式

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

输出格式

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

样例输入

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

样例输出

NO
NO

解题思路

  • 符合题意的数只能最多只能有三种,即a-k,a,a+k;多一种则不满足
  • 通过对输入的数组去重来计算种类。
    • 种类数<3,则一定YES;
    • 种类数>3,则一定NO;
    • 种类数=3,则判断是否为a-k,a,a+k;是则YES,否则NO;

完整代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
long long a[100010]; 
long long aa[100010];  
//去重后数目大于3则不可能  
int n,t,Count;    
int main()
{
	int t;
	cin>>t;
	while(t--)
	{	Count=1;
		cin>>n;
		for(int i=0; i<n; i++)
			cin>>a[i];   
	
		sort(a,a+n);//排序后去重
		aa[0]=a[0];//第一个一定在里面 
		int l=0,r=1; 
		while(l<n)     
		{
			if(a[l]!=a[r])//找到新的不等于的数 
			{
				l=r;//l移到当前位置,记录新数 
				aa[Count]=a[l];
				Count++;
			}
		
			r++;
		}

		if(Count<4||(Count==4&&aa[0]+aa[2]==2*aa[1]))
			cout<<"YES"<<endl;
		else
			cout<<"NO"<<endl;
	}
	return 0;
}

T2 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则右指针右移;到了则break;
  • 每次判断end处元素:
    • 是’?'则右移,长度++;
    • 是没到过的数则标记为到过,长度++,右移;
    • 是到过的数,则从左指针到当前位置,所有数依次标记为未到达(除问号)、长度–,
  • 最后判断长度是否为26;是则输出当前序列,遇到问号则从未到达的字母中从小到大取出一个并标记为到达;否则输出-1;

完整代码

#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
string a;
int reach[30];

int main()
{

	for(int i=0;i<27;i++)
		reach[i]=0;
	int qposition=0;	
	long long begin=0;
	long long end=0;
	long long length=0;
	cin>>a;  
	long long len=a.size();
	int in;

	while(end<len)
	{
		
		if(a[end]=='?')
			{
			end++;//后移 
			length++;//长度++ 
			}		
		
		else//不是问号 
		{ 
			in=a[end++]-'A';
			
		if(reach[in]==0)	
			{
			reach[in]=1;
			length++;
			}
		else//右边找到了到过的,把begin弄到左边第一个的后面 
		{int m;
	
			m = a[begin++]-'A';
	    		while(m!=in){
	    			if(a[begin-1]!='?')
	    				reach[m] = 0;
	    			length--;
	    			m = a[begin++]-'A';
	    		}}}
/*		while(a[begin]!=a[end])
			{if(a[begin]!='?')
		{	m=a[begin]-'A';	
			reach[m]=0;}
			begin++;
			length--;}
		}end++;begin++;
	*/
		if(length==26)
			break;
	
	}
	
	if(length==26)
			{
			for(int i=begin;i<end;i++)
				{if(a[i]=='?')
				{	while(reach[qposition]!=0)
						qposition++;
				cout<<char(qposition+'A');
				reach[qposition]=1;
				continue;}
				cout<<a[i];
				}
				cout<<endl;
			return 0;}
			
		cout<<"-1"<<endl;

}

T3 咕咕东的奇妙序列

问题描述

咕咕东 正在上可怕的复变函数,但对于稳拿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)
接下来第i+1行表示第i个输入k_i,表示询问第k_i
项数字。(1<=k_i<=10^{18})(1<=k i​<=10 ^18 )

输出格式

输出包含q行
第i行输出对询问k_ik i的输出结果。

样例输入

5
1
3
20
38
56

样例输出

1
2
5
2
0

解题思路

  • 判断第k位对应的数字,可以从大到小,先判断k是几位数,再二分判断k属于哪一块,再判断k在该块中对应第几个数,再判断k在该数中是第几位
  • a数组表示 最多表示i位数的 能表示数字的最大位数和,sum1[i]=sum1[i-1]+9*10^(i-1)*i;
  • b数组表示最多表示i位数的数位总数,sum2[i]=sum2[i-1]+等差数列的和(等差数列的首项为sum1[i-1]+i,公差为i,项数为9*10^(i-1))
  • start数组表示第i位数的首项
  • 判断几位数,即与b[i]进行比较。找到最后一个小于k的b[i],k减去它可以得到在当前大组中的位数,同时得到该数据所处大组中的上下界。
  • 有了上下界,可以二分搜索k的具体块,再找到k在区间中的位置,判断位数即可。

完整代码

#include <iostream>
using namespace std;

long long a[10];//最多i位数 能表示的最多位数 ,a[1]=9,a[2]=90*1+90*2=189......
long long b[10];//最多表示i位数 位数的和,b[1]=45,b[2]=1+2+...+9+11+13+...=9045 
long long start[10];
long long n;



int main()
{
	int q;
	cin>>q;
	
	start[0]=1;
	n=9;
	for(int i=1;i<=9;i++)
	{
		start[i]=start[i-1]*10;//每行第一个数的索引 
		a[i]=a[i-1]+(n*start[i-1]*i);//i是位数 
	}
	
	long long a1;
	int d;
	
		for(int i=1;i<=9;i++)
	{
		a1=a[i-1]+i;//a1是首项,i表示位数 
		d=i;
		b[i]=b[i-1]+((a1*n)+((n*(n-1)*d)/2));
		n=n*10;
	}
	
	while(q--)
	{
		long long k;
		cin>>k;
		int i=1;
		for(;k>b[i];i++)	//找到k的位数 
		{}
		
		k=k-b[i-1];//k在该区间内的位置
		//二分查找
		 
		long long l=start[i-1];
		long long r=start[i]-1;
		long long ans=1;
		long long temp1=l;
		a1=a[i-1]+i;
		d=i;

		
		while(l<=r)//二分查找
		{
			long long mid=(l+r)>>1;
			n=mid-temp1+1;
			
			if(a1*n+((n*(n-1)*d)/2)<k)
			{
				l=mid+1;
			}
			else
			{
				r=mid-1;
				ans=mid;
			}
		} 

		
		n=ans-start[i-1];//该行在该区间的位置
		k=k-((a1*n)+((n*(n-1)*d)/2));//k在单个区间的位置 
		int j;
		n=9;
		long long temp2;
		for(j=1;j<=9;j++)
		{
			temp2=n*j;
			n=n*10;
			if(k<=temp2)
			{
				break;
			}
			k=k-temp2;
		}
		//判断在数字内的位置
		if(k%j==0)//是整数 
		{
			long long m=(start[j-1]-1+(k/j))%10;
			cout<<m<<endl;
		} 
		else //查找属于十进制下的哪一个数
		{
			long long r=start[j-1]+(k/j);
			for(int p=j-(k%j);p>0;p--)
				r=r/10;
		
			long long m=r%10;
			cout<<m<<endl; 
		}
	}
} 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值