A - HRZ 的序列
题意
给定一个序列a,问是否存在一个数K,使得a中的每一个数 + K 、 − K 或 不 变 +K、-K或不变 +K、−K或不变,最终a中每一个数都相等。
解题思路
我们很容易知道,这样的序列只有2种可能:
- 序列中一共有2个数,K = 这两个数的平均值。
- 序列中有3个数,且其中一个数是另外两个数的平均数。
我们对a进行排序,之后判断中间的数是否为最大、最小或最大最小的平均数即可。
代码
#include <cstdio>
#include <iostream>
#include <vector>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
long long a[10010];
int main(){
int t;
cin >> t;
while(t--){
int n;
cin >> n;
for(int i = 0; i < n; i++){
cin >> a[i];
}
sort(a, a + n);
long long k = (a[n-1] - a[0])/2;
bool flag = true;
for(int i = 0; i < n; i++){
if(a[i] != a[0] && a[i] != a[n-1]){
if(((a[n-1] - a[0])%2) || (a[i] != a[0]+ k))
flag = false;
break;
}
}
if(flag) cout << "YES\n";
else cout << "NO\n";
}
return 0;
}
B - HRZ 学英语
题意
给定一个字符串,字符串中包含26个大写字母和 ′ ? ′ '?' ′?′,问是否存在一个长度为26的连续子串,包含26个不同字母,其中 ′ ? ′ '?' ′?′可以代替任意字母。若存在,则输出符合要求的字典序最小的子串。
解题思路
我们可以对字符串中从第一个字符到第l-25个(l为字符串长度)字符,以当前字符为首的长度为26的子串进行逐一判断,可以使用数组来记录字串中每一个字母的数量,用一个变量来记录
′
?
′
'?'
′?′的数量,对每一个子串进行判断,未出现的字母数量若与
′
?
′
'?'
′?′数量相等,则进行输出子串。
对于子串的最小字典序,首先将固定的字母填入答案串中,然后从末尾开始,将未填入的字母中最大的填入最右端的问号中,直至填充完成。(我为什么不从头开始填充问号= =,可能考场上脑子瓦特了)
代码
#include <cstdio>
#include <iostream>
#include <vector>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
char str[1000005];
int cnt[26];
int num;
int ansi;
char ans[26];
bool flag_ans[26];
bool flag = false;
int main(){
for(int i = 0; i < 26; ++i){
ans[i] = '?';
}
scanf("%s",str);
int len = strlen(str);
for(int i = 0; i < 26; i++){
if(str[i] != '?')
cnt[str[i] - 'A']++;
else{
num++;
}
}
int num1 = 0;
for(int i = 0; i < 26; i++){
if(cnt[i] == 0){
num1++;
}
}
if(num1 == num){
flag = true;
goto out;
}
for(int i = 26; i < len; i++){
if(str[i] == str[i-26])
continue;
else{
if(str[i] != '?')
cnt[str[i] - 'A']++;
else
num++;
if(str[i-26] != '?')
cnt[str[i-26] - 'A']--;
else
num--;
}
num1 = 0;
for(int i = 0; i < 26; i++){
if(cnt[i] == 0){
num1++;
}
}
if(num1 == num){
flag = true;
ansi = i - 25;
break;
}
}
out:
if(flag){
for(int i = ansi; i < ansi+26; i++){
if(str[i] != '?'){
ans[i - ansi] = str[i];
flag_ans[str[i]-'A'] = true;
}
}
for(int i = 25; i >= 0; i--){
if(ans[i] == '?'){
for(int j = 25; j >= 0; j--){
if(flag_ans[j] == false){
flag_ans[j] = true;
ans[i] = 'A' + j;
break;
}
}
}
}
cout << ans;
}else
cout << -1;
return 0;
}
C - 咕咕东的奇妙序列
题意
有一个序列:112123123412345123456… 这个序列由连续正整数组成的若干部分构成,其中第一部分包含1至1之间的所有数字,第二部分包含1至2之间的所有数字,第三部分包含1至3之间的所有数字,第i部分总是包含1至i之间的所有数字。
问数列的第k项是几。
解题思路
首先在考场上我想到的是前缀和的做法,sum[i]表示第i组及第i组之前的位数之和,首先求出sum[i],然后对于每一个输入的k,先二分法计算出第k项位于第几组,再在求出它在这个组的第几位即可。但是这个方法有一个问题就是数据规模过大达到10^18,求取前缀和的时间复杂度为O(N),空间占用也大,既超时,又超内存,只能拿60%的分数。(最后差一点调出来,一分没拿,实际上2分钟交一个只判断56位的也有30分,下次要改一改策略)
我想出来的正解是基于前缀和的方法优化的,前缀和没有必要一次性求出并存储,可以用优化的算法非常快速的动态求取,需要用的时候再求。我们知道第1-9组,每一组的位数+1,第10到99组,每一组位数+2,第100到999组,每一组位数+3,以此类推,再根据等差数列求和公式,我们能够在log的时间内求出需要的sum[i]并返回。
计算出在第几组后可以使用类似的方法计算出第k项在这组的哪个数中,最后直接输出该数的对应位即可(这里可以使用to_string函数来简化操作)。
代码
#include <iostream>
#include <cmath>
#include <string>
using namespace std;
long long lg(long long a){
long long k = 0;
while(a / 10){
k++;
a /= 10;
}
return k;
}
long long power(long long a){
if(a == 0) return 0;
long long sum1 = 1;
while(a--){
sum1 *= 10;
}
return sum1;
}
long long getSum(long long x){
long long ans = 0, a = 0, b = 1;
int time = lg(x)+1;
while(true){
if(b == time) break;
ans = ans + (a + a + b * (power(b)-1 - power(b-1)))*(power(b) - power(b-1)) / 2;
a = a + b * (power(b)-1 - power(b-1)) + b + 1;
b++;
}
ans = ans + (a + a + b * (x - power(b-1)))*(x - power(b-1) + 1) / 2;
return ans;
}
long long solve(long long k)
{
long long i;
int l = 1, r = 1000000000;
while(l <= r){
int mid = (l + r) >> 1;
if(getSum(mid) >= k){
i = mid;
r = mid - 1;
}
else l = mid + 1;
}
long long cur = k - getSum(i-1);
long long mark = 0,temp;
long long j;
for(j = 0; ; j++){
temp = mark;
if(j == 0)
mark += 9;
else
mark += (j + 1)*(power(j+1)-power(j));
if(mark >= cur)
break;
}
long long t = (cur - temp)%(j+1);
long long ans = (cur - temp - 1)/(j+1) + power(j);
if(j == 0)
ans++;
if(t!=0)
cout << to_string(ans)[t-1] << endl;
else
cout << to_string(ans)[j] << endl;
}
int main(){
long long q, k;
cin >> q;
while(q--){
cin >> k;
solve(k);
}
return 0;
}