文章目录
A - HRZ 的序列
相较于咕咕东,瑞神是个起早贪黑的好孩子,今天早上瑞神起得很早,刷B站时看到了一个序列aa,他对这个序列产生了浓厚的兴趣,他好奇是否存在一个数KK,使得一些数加上KK,一些数减去KK,一些数不变,使得整个序列中所有的数相等,其中对于序列中的每个位置上的数字,至多只能执行一次加运算或减运算或是对该位置不进行任何操作。由于瑞神只会刷B站,所以他把这个问题交给了你!
输入
输入第一行是一个正整数tt表示数据组数。接下来对于每组数据,输入的第一个正整数nn表示序列aa的长度,随后一行有nn个整数,表示序列aa。
输出
输出共包含tt行,每组数据输出一行。对于每组数据,如果存在这样的KK,输出"YES",否则输出“NO”。(输出不包含引号)
样例输入
2
5
1 2 3 4 5
5
1 2 3 4 5
样例输出
NO
NO
思路
综述
需要判断有几种不同的数字,如果不同的数字个数超过三个则输出NO;
如果正好是三个则判断是否据中间的数字左右对称,若是则输出YES,否则输出NO;
此外输出YES;
总结
1、思路较简单,数据范围是需要铭记的
2、数据范围longlong问题,大坑
代码
#include <iostream>
#include <algorithm>
using namespace std;
long long t;
long long n;
long long x, y, z;
long long a[10050];
long long tot = 0;
long long flag1, flag2;
int main() {
cin >> t;
for (long long i = 0; i < t; i++) {
cin >> n;
tot = 0;
flag1 = 0;
flag2 = 1;//判断是否有多个
for (long long j = 0; j < n; j++) {
cin >> a[j];
if (flag2 == 0) {
continue;
}
if (flag1 == 0){
x = a[j];
flag1++;
continue;
}if (flag1 == 1 && a[j] != x) {
flag1++;
y = a[j];
continue;
}if (flag1 == 2 && a[j] != x && a[j] != y) {
z = a[j];
flag1++;
continue;
}
if (flag1 == 3 && a[j] != x && a[j] != y && a[j] != z) {
flag2 = 0;
}
}
if (flag2 == 0) {
cout << "NO" << endl;
}
else {
if (flag1 < 3)cout << "YES" << endl;
else {
if (x > y)swap(x, y);
if (x > z)swap(x, z);
if (y > z)swap(y, z);
long long num1 = z - y;
long long num2 = y - x;
if (num1 == num2)cout << "YES" << endl;
else cout << "NO" << endl;
}
}
}
}
B - HRZ 学英语
瑞神今年大三了,他在寒假学会了英文的26个字母,所以他很兴奋!于是他让他的朋友TT考考他,TT想到了一个考瑞神的好问题:给定一个字符串,从里面寻找连续的26个大写字母并输出!但是转念一想,这样太便宜瑞神了,所以他加大了难度:现在给定一个字符串,字符串中包括26个大写字母和特殊字符’?’,特殊字符’?'可以代表任何一个大写字母。现在TT问你是否存在一个位置连续的且由26个大写字母组成的子串,在这个子串中每个字母出现且仅出现一次,如果存在,请输出从左侧算起的第一个出现的符合要求的子串,并且要求,如果有多组解同时符合位置最靠左,则输出字典序最小的那个解!如果不存在,输出-1!这下HRZ蒙圈了,他刚学会26个字母,这对他来说太难了,所以他来求助你,请你帮他解决这个问题,报酬是可以帮你打守望先锋。说明:字典序先按照第一个字母,以A、B、C…Z的顺序排列;如果第一个字母一样,那么比较第二个、第三个乃至后面的字母。如果比到最后两个单词不一样长(比如,SIGH和SIGHT),那么把短者排在前。例如
AB??EFGHIJKLMNOPQRSTUVWXYZ
ABCDEFGHIJKLMNOPQRSTUVWXYZ
ABDCEFGHIJKLMNOPQRSTUVWXYZ
上面两种填法,都可以构成26个字母,但是我们要求字典序最小,只能取前者。注意,题目要求的是第一个出现的,字典序最小的!
输入
输入只有一行,一个符合题目描述的字符串。
输出
输出只有一行,如果存在这样的子串,请输出,否则输出-1
样例输入
ABC??FGHIJK???OPQR?TUVWXY?
AABCDEFGHIJKLMNOPQRSTUVW??M
样例输出
ABCDEFGHIJKLMNOPQRSTUVWXYZ
-1
思路
综述 & 过程
尺取法,取26个相邻字母[L,R]看是否符合
1、如果出现两个相同的字母,则不符合,L++,R++;
2、如果符合,输出
输出要求:
从L开始输出,遇到非问号就输出,否则就输出不存在的字母的第一个,这样保证了字典序是最小的
总结
1、遇到大坑,相邻组数据之间需要初始化操作;
2、尺取法
代码
没必要统计问号个数,只需要判断是否存在两个同的字母即可
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
string s;
int num[26];
int main() {
cin >> s;
int l = 0;
int r = 25;
int flag = true;
for (int i = 0; i < 26; i++)num[i] = 0;
while (r < s.size()) {
for (int i = 0; i < 26; i++)num[i] = 0;
int wenhao = 0;
flag = true;
for (int i = l; i <= r; i++) {
if (s[i] == '?') {
wenhao++;
continue;
}
else {
int x = s[i] - 'A';
num[x]++;
if (num[x] > 1) {
flag = false;
break;
}
}
}
if (flag == false) {
l++;
r++;
continue;
}
else {
char ch;
for (int m = l; m <= r; m++) {
if (s[m] != '?')cout << s[m];
else {
for (int k = 0; k < 26; k++) {
if (num[k] != 0)continue;
else {
ch = 'A' + k;
cout << ch;
num[k] = 1;
break;
}
}
}
}
flag = true;
break;
}
}
if (flag == false)cout << "-1" << endl;
}
C - 咕咕东的奇妙序列
咕咕东正在上可怕的复变函数,但对于稳拿APlusAPlus的咕咕东来说,她早已不再听课,此时她在睡梦中突然想到了一个奇怪的无限序列:112123123412345…112123123412345…这个序列由连续正整数组成的若干部分构成,其中第一部分包含11至11之间的所有数字,第二部分包含11至22之间的所有数字,第三部分包含11至33之间的所有数字,第ii部分总是包含11至ii之间的所有数字。所以,这个序列的前5656项会是1121231234123451234561234567123456781234567891234567891011212312341234512345612345671234567812345678912345678910,其中第11项是11,第33项是22,第2020项是55,第3838项是22,第5656项是00。咕咕东现在想知道第kk项数字是多少!但是她睡醒之后发现老师讲的东西已经听不懂了,因此她把这个任务交给了你。
输入
输入由多行组成。
第一行一个整数qq表示有qq组询问(1<=q<=500)(1<=q<=500)
接下来第i+1i+1行表示第ii个输入kiki,表示询问第kiki项数字.(1<=ki<=1018)(1<=ki<=1018)
输出
输出包含q行
第ii行输出对询问的输出结果。
样例输入
5
1
3
20
38
56
样例输出
1
2
5
2
0
思路
当时拿到题,还有一个小时时间,先是写了个枚举前30分的算法,然后又写了枚举60分。虽然没拿到60分,只拿了30分,但是这种思想还是蛮重要的,即分步得分;
根据等差数列的性质计算。虽然看起来与等差数列不同,但是仍然是等差数列。其中数列的通项为1,2,……x的长度,数列和为长度相加即总长度。
在第1到第9块,每相邻的两块之间数字个数相差1;
第10块到第99块,每相邻的两块之间数字个数相差2;
第10i块到10i+1-1块,每相邻的两块之间数字个数相差i + 1。
找位置,用二分的方法。
一次用来查找它在第几块,一次用来查找它在块中的位置,就能找出它的值。
总结
1、二分法的应用应熟练;
2、分布得分,写枚举算法可以拿到大半的分数,如果时间不够或者思路缺少注意使用;
(类似模板)如下所示:
#include <iostream>
using namespace std;
int main(){
scanf("%d%d",&n,&m);
if(n<=10 && m<=10) test1();
else if (n<=50 && m<=50 ) test2();
else if (n<=300 && m<=300 ) test3();
//...
}
void test1(){
}
void test2(){
}
void test3(){
}
代码
上文有详细注释
#include<iostream>
using namespace std;
long long q,k;
const long long maxn=1e9;
long long getsum(long long x){
return x*(x+1)/2;
}
long long sum1(long long x){
long long ans =0,i=1,j=1;
for ( ; j*10<=x; i++,j*=10 ) {
ans += i*getsum(j*9) + i*j*9*(x-j*10+1);
}
return ans + i*getsum(x-j+1);
}
long long sum2 ( long long x ) { // 获取sum[x]
long long ans=0, i=1, j=1;
for ( ; j*10<=x; i++,j*=10 ) {
ans += i*j*9;
}
return ans + i*(x-j+1);
}
long long location(long long k){
long long left,right;
long long ans1,ans2;
left=0;
right=maxn;
while(left<=right){
long long mid = (left+right)/2;
if(sum1(mid)<k){
left = mid+1;
ans1 = mid;
}
else right = mid-1;
}
k -= sum1(ans1);
left = 0;
right = ans1+1;
while(left<=right){
long long mid = (left+right)/2;
if(sum2(mid)<k){
left=mid+1;
ans2 = mid;
}else right = mid-1;
}
k -=sum2(ans2++);
int a[30];
for(int i=0;i<30;i++)a[i]=0;
int i = 0;
while(ans2) {
a[++i] = ans2%10;
ans2 /= 10;
}
return a[i-k+1];
}
int main(){
cin>>q;
for(int i=0;i<q;i++){
cin>>k;
cout<<location(k)<<endl;
}
}