CSP-M2 补题

A - HRZ 的序列

B - HRZ 学英语

C - 咕咕东的奇妙序列

A - HRZ 的序列

题目

在这里插入图片描述

思路

将所有数字放入集合中记录出现的数字种类。若数字种类小于等于3,则满足要求,若大于3,则不满足要求。

代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <set> 
#define llint long long
#define maxn 10010
using namespace std;

int t,n;
llint a[4];
set<llint> s;
int main() {
	cin>>t;
	for(int j=0;j<t;j++) {
		cin>>n;
		llint temp;
		for(int i=0;i<n;i++) {
			scanf("%lld",&temp);
			s.insert(temp);
		}
		if(s.size()>3) printf("NO\n");
		else if(s.size()<3) printf("YES\n");
		else if(s.size()==3) {
			int k=0;
			for(set<llint>::iterator it=s.begin(); it!=s.end(); it++) {
				k++;
				a[k]=*it;
			}
			sort(a+1,a+4);
			if(a[1]+a[3]==2*a[2]) printf("YES\n");
			else printf("NO\n");
		}
		s.clear(); 
	}
	return 0;
}

B - HRZ 学英语

题目

在这里插入图片描述

思路

尺取法。字符串取一个长度为26的区间,维护vis数组记录字母出现的次数,已经出现的字母种类数cnt1,还要记录’?‘出现的次数cnt2。如果cnt1+cnt2=26,说明可以通过改变’?'来满足要求,则输出答案。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <set> 
using namespace std;

set<char> st;
int vis[26];
bool flag=false;
int Left=0,Right=0,ans=-1,cnt1=0,cnt2=0;
string s;

void output() {
	for(int i=0;i<26;i++) {
		if(s[i+Left]!='?') cout<<s[i+Left];
		else { //补上当前最小缺值 
			for(int j=0;j<26;j++) {
				if(vis[j]==0) {
					vis[j]=1;
					cout<<char('A'+j);
					break;
				}
			} 
		}
	}
	cout<<endl;
}

int main() {
	cin>>s;
	memset(vis,0,sizeof vis);
	//初始窗口 
	Left=0,Right=25;
	for(int i=0;i<26;i++) {
		if(s[i]=='?') cnt2++;
		else {
			if(vis[s[i]-'A']!=0) vis[s[i]-'A']++;
			else {
				vis[s[i]-'A']++;
				cnt1++;
			}
		} 
	}
	
	while(Right<=s.size()-1) {
		if(cnt1+cnt2==26) {
			output();
			flag=true;
			break;
		} 
		if(Right==s.size()-1) break;
		if(s[Left]=='?') cnt2--;
		else {
			vis[s[Left]-'A']--;
			if(vis[s[Left]-'A']==0) cnt1--;
		}
		if(s[Right+1]=='?') cnt2++;
		else {
			if(vis[s[Right+1]-'A']==0) cnt1++;
			vis[s[Right+1]-'A']++;
		}
		Left++;
		Right++;
	}
	
	if(flag==false) cout<<ans<<endl;
	return 0;
}

C - 咕咕东的奇妙序列

题目

在这里插入图片描述

思路

●暴力一个一个部分地减掉会超时。。。第i个部分包含1-i的数字,使用二分查找第k个数字属于哪个部分,并将k减去该部分之前的数字长度,再在那个部分中用二分查找k属于哪个数字,k再减去该数字之前的数字长度,此时答案便是该数字中的第k位数字。
●假设k属于第x部分,则k大于前x个部分的长度和,小于前x+1个部分的长度和。二分时,不断将x的前缀和与k比较。在计算前x个部分的长度和时,因为一个部分的长度和与数字位数有关,所以需要根据第i个部分的i的位数进行计算。

代码

#include<iostream>
#include<cstring>
#include<cmath> 
using namespace std;
long long int ans[50];
long long int find1(long long int x) { //求尾项为x的数列的前缀和 
 	long long int a1=1,d=1,n,ans=0,num=1;
	while(x>=10*num) {
		num*=10; //位数 
		n=num-num/10; //项数 
		ans+=n*a1+n*(n-1)*d/2; //该层数字的数量 
		a1+=n*d+1; //新的首项 
		d++; //新的公差 
	}
	n=x-num+1;
	ans+=n*a1+n*(n-1)*d/2;
	return ans;
}

long long int find2(long long int x) {
	long long int ans=0,d=1,num=1;
	while(x>=10*num) {
		ans+=9*num*d;
		num*=10;
		d++;
	}
	ans+=(x-num+1)*d;
	return ans;
}

int q;
long long int k,n,l,r,mid,temp;

void solve(long long int k) {
	memset(ans, 0, sizeof ans);
	l = 0, r = 1e9;
	while (l <= r) {
		mid = (l + r) / 2;
		temp = find1(mid);
		if (temp < k) {
			n = mid;
			l = mid + 1;
		}
		else r = mid - 1;
	}
	k -= find1(n);
	l = 0, r = n;
	while (l <= r) {
		mid = (l + r) / 2;
		temp = find2(mid);
		if (temp < k) {
			n = mid;
			l = mid + 1;
		}
		else r = mid - 1;
	}
	k -= find2(n);
	n++;
	int t = 0;
	while (n) {
		ans[t++] = n % 10;
		n /= 10;
	}
	cout << ans[t - k] << endl;
}
int main() {
	cin>>q;
	for(int i=0;i<q;i++) {
		cin>>k;
		solve(k);
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值