SDU 程序设计思维与实践 week8 CSP模拟

注:所有题目限制均为1s,64MB

1-HRZ的序列

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

输入格式

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

输出格式

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

输入样例

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

输出样例

NO
NO

数据说明

数据点(上限) t t t n n n a i a_i ai
1,2101010
3,4,510 1 0 3 10^3 103 1 0 9 10^9 109
6,7,8,9,1010 1 0 4 10^4 104 1 0 15 10 ^ {15} 1015

易错点

  • 数据范围是 1 0 15 10^{15} 1015,需要使用long long 表示

思路分析

  • 将所有元素排序。
  • 如果序列中只有一种或两种不同的数则输出YES
  • 如果有三种不同的数且他们的差相等,则输出YES

代码

#include<iostream>
#include<cstdio>
#include<queue>
#include<algorithm>
#include<vector>
#include<cstring>
#include<string>
using namespace std;
int t,n,ans,dif; 
long long a[10005];
long long temp,l,r,m; 
int main(){
	cin>>t;
	while(t--){
		ans=0;dif=1;
		cin>>n;
		for(int i=0;i<n;i++){
			cin>>a[i];
		}
		sort(a,a+n);
		temp=a[0],l=a[0],r=a[n-1],m=a[n-1];
		//1、所有数字都相等 
		if(a[0]==a[n-1]){
			cout<<"YES"<<endl;
			continue; 
		}
		for(int i=1;i<n;i++){
			if(a[i]!=temp){
				if(dif==1){
					m=a[i];
				}
				else if(dif==2){
					r=a[i];
				}
				dif++;
				temp=a[i];
			}	
		}
		//2、只有两种数 
		if(dif==2){
			cout<<"YES"<<endl;
			continue; 
		}
		//3、三种不同的数
		else if(dif==3&&r-m==m-l){
			cout<<"YES"<<endl;
			continue; 
		} 
		cout<<"NO"<<endl; 
	}
	return 0;
} 

2-HRZ学英语【尺取】

瑞神今年大三了,他在寒假学会了英文的26个字母,所以他很兴奋!于是他让他的朋友TT考考他,TT想到了一个考瑞神的好问题:给定一个字符串,从里面寻找连续的26个大写字母并输出!但是转念一想,这样太便宜瑞神了,所以他加大了难度:现在给定一个字符串,字符串中包括26个大写字母和特殊字符’?’,特殊字符’?'可以代表任何一个大写字母。现在TT问你是否存在一个位置连续的且由26个大写字母组成的子串,在这个子串中每个字母出现且仅出现一次,如果存在,请输出从左侧算起的第一个出现的符合要求的子串,并且要求,如果有多组解同时符合位置最靠左,则输出字典序最小的那个解!如果不存在,输出-1! 这下HRZ蒙圈了,他刚学会26个字母,这对他来说太难了,所以他来求助你,请你帮他解决这个问题,报酬是可以帮你打守望先锋。
说明:字典序 先按照第一个字母,以 A、B、C……Z 的顺序排列;如果第一个字母一样,那么比较第二个、第三个乃至后面的字母。如果比到最后两个单词不一样长(比如,SIGH 和 SIGHT),那么把短者排
在前。例如

AB??EFGHIJKLMNOPQRSTUVWXYZ

ABCDEFGHIJKLMNOPQRSTUVWXYZ
ABDCEFGHIJKLMNOPQRSTUVWXYZ

上面两种填法,都可以构成26个字母,但是我们要求字典序最小,只能取前者。
注意,题目要求的是 第一个出现的,字典序最小的!

输入格式

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

输出格式

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

样例输入

ABC??FGHIJK???OPQR?TUVWXY?

样例输出

ABCDEFGHIJKLMNOPQRSTUVWXYZ

数据说明

数据点字符串长度
1,2,326
4,5,6 1 0 4 10^4 104
7,8,9,10 1 0 6 10^6 106

错题点

当右指针指向的元素已经出现时,左指针该如何变化。

思路分析

设左右指针l和r,r为工作指针,从左到右扫描字符串。

  • 如果l和r取的范围>26,左指针左移,并处理丢弃的元素。
  • 如果r对应?,问号数++
  • 如果r对应的字母已经出现,左指针向左移动到出现r对应的字母的下一个位置,并处理丢弃的元素
  • 如果r对应的字母没有已经出现,标记已经出现。
  • 最后判断是否满足条件
#include<iostream>
#include<cstdio>
#include<queue>
#include<algorithm>
#include<vector>
#include<cstring>
#include<string>
using namespace std;
string s;
int l=0,r=0;
int alp[26],numofw,ans; 
int main(){
	cin>>s;	
	while(r<s.length()){	
		//1、是否超界
		if(r-l>25){			
			if(s[l]=='?'){
				numofw--;
			}
			else{
				alp[s[l]-'A']=0;
			}
			l++;			
		}
		//2、处理r				
		if(s[r]=='?'){//问号 
			numofw++;
		} 
		else if(alp[s[r]-'A']==1){//已经有了这个字母了   bug点!! 
			while(s[l]!=s[r]){
				if(s[l]=='?'){
					numofw--;
				}
				else{
					alp[s[l]-'A']=0;
				}			
				l++;	
			}
			l++;			
		}
		else {//这个字母还没有 
			alp[s[r]-'A']=1;
		}
		//3、判断是否满足条件
		if(r-l==25){
			int nothave=0;
			for(int i=0;i<=25;i++){
				if(alp[i]==0) nothave++;
			}
			if(nothave==numofw){				
				ans=1;
				break;
			}
		} 
		//4、r++
		r++; 
	}	
	if(ans==1){//输出函数
		for(int i=l;i<=r;i++){
			if(s[i]=='?'){
				for(int j=0;j<=25;j++){
					if(alp[j]==0){
						s[i]='A'+j;alp[j]=1;
						break;
					}
				}
			}
			cout<<s[i];
		}
		cout<<endl;		
	}
	else cout<<-1<<endl;
	return 0;
} 

3-咕咕东的奇妙序列【二分】

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

输出格式

输出包含q行 第i行输出对询问 k i k_i ki的输出结果。

样例输出

5
1
3
20
38
56

样例输出

1
2
5
2
0

数据说明

数据点qk
1,2,350055
4,5,6 100 100 100 1 0 6 10^6 106
7,8,9,10 500 500 500 1 0 18 10^{18} 1018

理论分析

  • s s u m [ x ] ssum[x] ssum[x]为序列从开头到第一次出现x的总长度, s u m [ x ] sum[x] sum[x]为1~x的总长度,那么有关系 ∑ k = 1 x s u m [ k ] = s s u m [ x ] \sum_{k=1}^xsum[k]=ssum[x] k=1xsum[k]=ssum[x]
  • [ 1 0 k , 1 0 k + 1 ) [10^k,10^{k+1}) [10k,10k+1)范围内的每一个数的长度都是 k + 1 k+1 k+1。所以 s u m [ x ] , x ∈ [ 1 0 k , 1 0 k + 1 ) sum[x],x \in [10^k,10^{k+1}) sum[x]x[10k,10k+1)构成了等差数列,公差为 d = k + 1 d=k+1 d=k+1
  • s s u m [ x ] ssum[x] ssum[x]的计算方法:利用等差数列求和公式 S n = n a 1 + n ( n − 1 ) / 2 ∗ d S_n=na_1+n(n-1)/2*d Sn=na1+n(n1)/2d.
  • s u m [ x ] sum[x] sum[x]的计算方法:利用等差数列的通项公式 a n = a p + ( n − p ) d a_n=a_p+(n-p)d an=ap+(np)d

实现分析

1、对于一个数x,计算 s s u m [ x ] ssum[x] ssum[x] s u m [ x ] sum[x] sum[x]采用 O ( l o g n ) O(logn) O(logn)的算法。将x分成1位数,2位数,…,n位数来计算,即1-9,10-99,100-999,…。每一次按照等差数列的公式计算。
2、二分求解 s s u m [ x ] < k ssum[x]<k ssum[x]<k的最大的x。那么答案在总序列中位于连续从1到x+1的的那一段。 k − = s s u m [ x ] k-=ssum[x] k=ssum[x]
3、二分求解 s u m [ x ] < k sum[x]<k sum[x]<k的最大的x。 k − = s u m [ x ] k-=sum[x] k=sum[x]那么答案是数x+1第k位。

代码

#include<iostream>
#include<cstdio>
using namespace std;
long long k,l,r,mid,ans;
int q;
long long get(long long x,int opt){
	long long pwr=1,ssum=0,sum=0,n=0,d=0;
	while(true){
		pwr*=10;d++;
		if(x>pwr-1){
			n=pwr-pwr/10;
			ssum+=(sum+d)*n+n*(n-1)/2*d;
			sum+=n*d;
		}
		else{
			n=x-pwr/10+1;
			ssum+=(sum+d)*n+n*(n-1)/2*d;
			sum+=n*d;
			break;
		}
	}
	return opt==1?ssum:sum;
}
int main(){
	cin>>q;
	while(q--){
		cin>>k;
		l=0;r=1e9;
		while(l<=r){//寻找<k的最大的 
			mid=(l+r)/2;
			if(get(mid,1)<k) ans=mid,l=mid+1 ;			
			else r=mid-1;						 
		}		
		k-=get(ans,1);
		l=0;r=ans+1;
		while(l<=r){//寻找<k最大的 
			mid=(l+r)/2;
			if(get(mid,2)<k) ans=mid,l=mid+1; 			
			else r=mid-1;			
		}
		k-=get(ans,2);
		//第ans+1个数的第k位
		ans++;	
		string s=to_string(ans);
		printf("%d\n",s[k-1]-'0');	
	}    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值