SDU程序设计第二次CSP模拟

A HRZ的序列

题目

题目Input&&Output
inputoutput
sample

#input:
2
5
1 2 3 4 5
5
1 2 3 4 5
#output:
NO
NO

题解

1.本题我们观察发现答案未知,但是有明确边界,而且有明确移动方向因此我们想到
可以二分答案
2.注意点:数据范围,本题应使用long long;其次还应注意插值等于0时的特判

C++代码

#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1e4+100;
long long t,n,ele[maxn];

void init(){
	for(int i=0;i<n;i++)
		ele[i] = 0;
}

int solve(){
	long long mn=ele[0],mx=ele[n-1],mid=(mn+mx)/2,k=0,i=0;
	while(mn<=mx){
		mid = (mn+mx)/2;
		k = min(mid - ele[n-1],mid - ele[0]);//k && k<=0
		for(i=0;i<n;i++){
			if(abs(mid-ele[i])==abs(k)||mid-ele[i]==0) continue;
			if(abs(mid-ele[i])>abs(k)) mn = mid+1;
			else mx = mid-1;
			break;
		}
		if(i == n){
			return 1;
		}
	}
	return 0;
}
int main(){
	cin>>t;
	while(t--){
		cin>>n;
		init();
		for(int i=0;i<n;i++) cin>>ele[i];
		sort(ele,ele+n);
		if(solve()) cout<<"YES"<<endl;
		else cout<<"NO"<<endl;
	}
	return 0;
}

B HRZ学英语

题目

题目
Input&&Output
input
output
sample

#input:
ABC??FGHIJK???OPQR?TUVWXY?
#output:
ABCDEFGHIJKLMNOPQRSTUVWXYZ

题解

1.本题乍一看很向尺取,区间左右有明确的移动方向,且答案与区间内值有关
2.当我们选择尺取之后应该注意区间移动的条件以及加入踢出区间的条件,其次?的
判断尤为重要,也是最后找出最小序的关键所在,本题我才采用一个数组记录?与其
它字母的情况然后依次填补空缺

C++代码

#include<iostream>
#include<string>
using namespace std;
const int maxn = 1e6+100;
string curr;
int ele[maxn],mark[27],l=0,r=-1;
void init(){
	for(int i=0;i<curr.length();i++){
		if(curr[i]=='?') ele[i] = 0;
		else ele[i] = curr[i] - 'A' + 1;
	}
	for(int i=0;i<27;i++) mark[i]=0;
}
int find(){
	for(int i=1;i<27;i++){
		if(mark[i]>1) return i;
	}
	return 0;
}
void solve(){
	for(int i=0;i<curr.length();i++){
		if(r-l+1<=26){
			r++;
			mark[ele[i]]++;
		}
		int m = find();
		if(m!=0){
			while(ele[l] != m){
				mark[ele[l]]--;
				l++;
			}
			mark[ele[l]]--;
			l++;
		}
		//此时若 r-l+1==26 则寻找序列
		int p=1;
		if(r-l+1==26){
			for(int j=l;j<=r;j++){
				if(curr[j]=='?'){
					while(mark[p]!=0) p++;
					cout<<char('A'+p-1);
					p++;
				}
				else cout<<char('A'+ele[j]-1);
			}
			cout<<endl;
			return;
		}
	}
	cout<<-1<<endl;
	return;
	
}
int main(){
	cin>>curr;
	init();
	solve();
	return 0;
}

C 咕咕东的奇妙序列

题目

题目
Input&&Output
inputoutput
sample

#input:
5
1
3
20
38
56
#output:
1
2
5
2
0

题解

1.本题自我感觉很神奇,至少我找了很久都没找出来规律
2.仔细分析之后发现本题实际上也是一个二分答案的过程
3.首先我看错了题以为只有 1 2 3 4 5......然后开始做题(但无意中也成了过程的
一部分,我们只观察这一部分可以发现数字占位是阶段性的(实际上是一个分段的
等差数列,因此我们按照位数划分如果给出kn就可以按照这种规律去分割,求每个
阶段的和最后加上一个不完整的阶段
4.然后发现本题实际上是上一个的重复 (1)( 1 2)( 1 2 3)
	刚刚找到的规律实际上是本题的第n项的规律,因此我开始思考如何求在第n段的
	kn,由于是连续数据显然暴力会超时,因此类比刚刚的这个实际上也是一个分段
	的等差数列,然鹅我们却不可以知道确切的值因此我们可以采用二分答案试出来
	n,然后再套用3的做法就可以正确解出题目
PS:注意范围 long long

C++代码

#include<iostream>
#include<math.h>
using namespace std;

long long maxn = 1e18+100;

long long get_number(long long kn,long long &w,long long &len){
	long long begin=1,n,num=0;
	len = 1;
	while(true){
		begin*=10;
		n=begin-begin/10; 
		if(kn<=n*len){
			num+=kn/len;
			w=kn%len;   //位数 作到右 
			return num;
		}else{
			num+=n;
			kn-=n*len;
		}
		len++;	
	}
}

void result(long long kn){
	long long w,len; 
	long long num = get_number(kn,w,len);
	if(w!=0)  num++; //防止进位 
	//cout<<num<<" : "<<w<<endl;
	//for(;len!=w&&w!=0;len--) num/=10;
	for(long long j=len-w;j>0&&w!=0;j--) num/=10;
	cout<<num%10<<endl;
}

long long get_num(int n){//获取一个数所处位置 等差数列(分段) (1) (1 2) (1 2 3)……(1 2 3...n) 
	long long begin = 1,d = 1,sum = 0,end = 0,nu = 0,a0 = 1;//n项数 sum和 end段的最后一项 begin段起始 a0首项 
	while(true){
		begin*=10;
		end = begin-1;
		if(n<=end)//本段可能未使用完
			nu = n-begin/10 + 1;
		else
			nu = end - begin/10 + 1;
		sum+=a0*nu + nu*(nu-1)*d/2;
		if(n<=end) return sum;
		d++;
		a0 = a0 + (nu-1)*(d-1) + d; 
	}
}

int get_group(long long kn){//前面区间的组号 
	int group,l=0,r=1e9,mid;
	while(l<=r){
		mid = (l+r)/2;
		if(get_num(mid) < kn){
			l = mid+1;
			group = mid;
		}else{
			r = mid-1;
		}
	}
	return group;
}

int main(){
	long long kn;
	int q;
	cin>>q;
	while(q--){
		cin>>kn;
		int group = get_group(kn);
		kn-=get_num(group);
		//cout<<"group : "<<group<<" kn : "<<kn<<endl;
		result(kn);
	}
	return 0; 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值