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;
}