A - HRZ的序列
题目描述
相较于咕咕东,瑞神是个起早贪黑的好孩子,今天早上瑞神起得很早,刷B站时看到了一个序列a,他对这个序列产生了浓厚的兴趣,他好奇是否存在一个数K,使得一些数加上K,一些数减去K,一些数不变,使得整个序列中所有的数相等,其中对于序列中的每个位置上的数字,至多只能执行一次加运算或减运算或是对该位置不进行任何操作。由于瑞神只会刷B站,所以他把这个问题交给了你!
Input
输入第一行是一个正整数t表示数据组数。
接下来对于每组数据,输入的第一个正整数n表示序列a的长度,随后一行有n个整数,表示序列a。
Output
输出共包含t行,每组数据输出一行。对于每组数据,如果存在这样的K,输出"YES",否则输出“NO”。(输出不包含引号)
Sample Input
2
5
1 2 3 4 5
5
1 2 3 4 5
Sample Output
NO
NO
解题思路
满足题意的数组只有三种情况:
(1)数组中只有一种数字;
(2)数组中只有两种数字;
(3)数组中有三种数字a, b, c(升序排列),并且满足 b - a == c - b;
输入序列a[ ],vector< int > s 用于存储出现的数字的种类;
遍历数组a,每出现一种新的元素就将其存入s中,s.size( )实际上就相当于出现的数字的种类;
对满足题意的三种情况做具体判断,当s.size( ) > 3 时,直接结束遍历,输出NO;
解题代码
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
using namespace std;
int t, n;
long long int a[10005];
vector<long long int> s;
int type;
int main() {
cin>>t;
for (int x = 0; x < t; x++) {
//初始化
s.clear();
cin>>n;
//序列a[]的初始化
for (int y = 0; y < n; y++) {
long long int number; cin>>number;
a[y] = number;
}
//判断
int symbol = 0;
for (int y = 0; y < n; y++) {
if (s.size() == 0) s.push_back(a[y]);
//不能出现超过三种的数字
if (s.size() > 3) {symbol = -1; break;}
else {
//与已经出现过的数进行比对
int yon = 0;
for (long int z = 0; z < s.size(); z++) {
//该数匹配成功
if (s[z] == a[y]) {yon = 1; break;}
//该数未匹配成功
else {yon = -1; continue;}
}
//该数是一个新数
if (yon == -1) s.push_back(a[y]);
}
}
if (s.size() == 3) {
sort(s.begin(), s.end());
if (s[1] - s[0] != s[2] - s[1]) symbol = -1;
}
if (symbol == -1) cout<<"NO"<<endl;
else cout<<"YES"<<endl;
}
return 0;
}
B - HRZ学英语
题目描述
瑞神今年大三了,他在寒假学会了英文的26个字母,所以他很兴奋!于是他让他的朋友TT考考他,TT想到了一个考瑞神的好问题:给定一个字符串,从里面寻找连续的26个大写字母并输出!但是转念一想,这样太便宜瑞神了,所以他加大了难度:现在给定一个字符串,字符串中包括26个大写字母和特殊字符’?’,特殊字符’?'可以代表任何一个大写字母。现在TT问你是否存在一个位置连续的且由26个大写字母组成的子串 ,在这个子串中每个字母出现且仅出现一次,如果存在,请输出从左侧算起的第一个出现的符合要求的子串,并且要求,如果有多组解同时符合位置最靠左,则输出字典序最小的那个解!如果不存在,输出-1!这下HRZ蒙圈了,他刚学会26个字母,这对他来说太难了,所以他来求助你,请你帮他解决这个问题,报酬是可以帮你打守望先锋。
说明:字典序 先按照第一个字母,以 A、B、C……Z 的顺序排列;如果第一个字母一样,那么比较第二个、第三个乃至后面的字母。如果比到最后两个单词不一样长(比如,SIGH 和 SIGHT),那么把短者排在前。
例如:
输入
AB??EFGHIJKLMNOPQRSTUVWXYZ
输出
ABCDEFGHIJKLMNOPQRSTUVWXYZ
ABDCEFGHIJKLMNOPQRSTUVWXYZ
上面两种填法,都可以构成26个字母,但是我们要求字典序最小,只能取前者。
Input
输入只有一行,一个符合题目描述的字符串。
Output
输出只有一行,如果存在这样的子串,请输出,否则输出-1
Sample Input
ABC??FGHIJK???OPQR?TUVWXY?
Sample Output
ABCDEFGHIJKLMNOPQRSTUVWXYZ
解题思路
从输入的字符串的第一个字符开始选择一段长度为26的字符串,然后将首位置往后移一位;
对选择的长度为26的字符串进行判断:
遍历这个字符串 w_number 存储 ? 的数目; z_number 存储字符的数目;fu[ ]存储已经出现了的字母;
遍历结束之后:
(1)w_number + z_number < 26,那么是不满足条件的;
(2)w_number + z_number == 26,从fu[ ]中获取未出现的字母,用一个队列can按照字典序存储;输出的时候,先输出已有的字母,在对应的 ? 的位置,输出队列can的队首元素;
解题代码
#include <iostream>
#include <string>
#include <queue>
using namespace std;
struct zi {
long int n; //在输入字符串里的位置
char m; //字符
}j[26];
int w_number = 0; //?个数
int z_numebr = 0; //字符个数
int fu[26]; //存储要输出字符
queue<char> can;
int main() {
string z; cin>>z;
int symbol = 0;
long int begin;
//获取一段长度为26的字符串
for (begin = 0; begin < z.size(); begin++) {
//判断是否继续
if (z.size() - begin < 26) break;
//初始化
for (int a = 0; a < 26; a++) fu[a] = 0;
w_number = 0; z_numebr = 0;
while (can.size() != 0) {can.pop();}
//存储这一段字符里面的元素
for (long int a = begin; a < begin + 26; a++) {
if (z[a] == '?') w_number++;
else {
int q = z[a] - 65;
if (fu[q] == 1) continue;
else {
fu[q] = 1;
j[z_numebr].n = a; j[z_numebr].m = z[a];
z_numebr++;
}
}
}
//进行判断
if (z_numebr + w_number < 26) symbol = -1;
else {
//确认空缺的元素
for (int b = 0; b < 26; b++) {
if (fu[b] == 0) {
char s = 'A' + b;
can.push(s);
}
}
//输出答案
if (j[0].n != begin) {
long int d = j[0].n - begin;
for (int f = 0; f < d; f++) {
cout<<can.front();
can.pop();
}
}
for (int f = 0; f < z_numebr; f++) {
cout<<j[f].m;
//中间有?号的情况
if (j[f+1].n != j[f].n + 1) {
long int d = j[f+1].n - j[f].n - 1;
for (int g = 0; g < d; g++) {
cout<<can.front();
can.pop();
}
}
}
while (!can.empty()) {cout<<can.front(); can.pop();}
return 0;
}
}
if (symbol == -1) cout<<-1<<endl;
return 0;
}
C - 咕咕东的奇妙序列
题目描述
咕咕东正在上可怕的复变函数,但对于稳拿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 项数字是多少!但是她睡醒之后发现老师讲的东西已经听不懂了,因此她把这个任务交给了你。
Input
输入由多行组成。
第一行一个整数q表示有q组询问(1<=q<=500)
接下来第 i+1 行表示第i个输入 ki ,表示询问第 ki 项数字。( 1<=ki <=10^18)
Output
输出包含 q 行
第i行输出对询问 ki 的输出结果。
Sample Input
5
1
3
20
38
56
Sample Output
1
2
5
2
0
解题思路
用数组a[ i ]存储 i 位数的区间个数, b[ i ] 存储 i 位数的最后一个区间的长度
对于 a[ l ] = ( (b[I - 1] + l ) + (b[l - 1] + 9 x pow(10, l - 1) x l ) ) * 9 x pow(10, l - 1) / 2;
(1)用二分法判断输入的k位于那个区间;
(2)按照1位数占9 · 1位,2位数占 90 · 2位…… 确定k在目标区间的具体位置;
(3)确定k所在位置的数的值,以及具体是第几位;
(4)所在位置数进行除10取余的操作,获得目标的输出值;
解题代码
#include <iostream>
#include <algorithm>
#include <string>
#include <cmath>
#include <vector>
using namespace std;
int q;
long long int ans;
vector<long long int> a, b;
void initialize () {
a.push_back(0); b.push_back(0);
long long int i = 1, all_num = 9;
while (a[i - 1] <= static_cast<long long int>(pow(10, 18))) {
//i位数的区间的总数
long long int x = ((b[i - 1] + i) + (b[i - 1] + all_num * i)) * all_num / 2;
//i位数的最后一个对应的区间长度
long long int y = b[i - 1] + all_num * i;
all_num *= 10;
a.push_back(x); b.push_back(y);
i++;
}
}
long long int get_ans (long long int k) {
//查找k是几位数
int l = 1;
while (k > 0) {k -= a[l]; l++;}
l--; k += a[l];
//查找是在l-1位数的区间内的哪一个区间
long long left = 1, right = 9 * static_cast<long long int>(pow(10, l - 1)), mid;
while (left <= right) {
mid = (left + right) / 2;
long long int sum = ((b[l - 1] + l) + (b[l - 1] + mid * l)) * mid / 2;
if (sum >= k) right = mid - 1;
else left = mid + 1;
}
k -= ((b[l - 1] + l) + (b[l - 1] + right * l)) * right / 2;
//确定k在目标区间的位置
long long int u = 1;
while (k > 0) {k -= 9 * static_cast<long long int>(pow(10, u - 1)) * u; u++;}
u--; k += 9 * static_cast<long long int>(pow(10, u - 1)) * u;
long long int shang = (k + u - 1) / u;
long long int yu = k - (shang - 1) * u;
long long mubiao = static_cast<long long int>(pow(10, u - 1)) + shang - 1;
for (long long int h = 0; h < u - yu; h++) mubiao = mubiao / 10;
ans = mubiao % 10;
return ans;
}
int main() {
cin>>q;
initialize();
while (q--) {
long long int k; cin>>k;
cout<<get_ans(k)<<endl;
}
return 0;
}