A - HRZ 的序列
时间限制 | 空间限制 |
---|---|
1s | 64mb |
题目描述
相较于咕咕东,瑞神是个起早贪黑的好孩子,今天早上瑞神起得很早,刷B站时看到了一个序列a,他对这个序列产生了浓厚的兴趣,他好奇是否存在一个数K,使得一些数加上K,一些数减去K,一些数不变,使得整个序列中所有的数相等,其中对于序列中的每个位置上的数字,至多只能执行一次加运算或减运算或是对该位置不进行任何操作。由于瑞神只会刷B站,所以他把这个问题交给了你!
输入格式
输入第一行是一个正整数t表示数据组数。接下来对于每组数据,输入的第一个正整数n表示序列a的长度,随后一行有n个整数,表示序列a。
输出格式
输出共包含t行,每组数据输出一行。对于每组数据,如果存在这样的K,输出"YES",否则输出“NO”。(输出不包含引号)
样例输入
2
5
1 2 3 4 5
5
1 2 3 4 5
样例输出
NO
NO
数据点(上限) | t | n | a_i |
---|---|---|---|
1,2 | 10 | 10 | 10 |
3,4,5 | 10 | 103 | 109 |
6,7,8,9,10 | 10 | 104 | 1015 |
分析
- 此题思路简单,主要是要用到set的去重性,通过set中size来分析。其中size<=3时才有可能输出YES。size=3时,中间大小的数不变,小于这部分的数加K,大于这部分的数减K,这就要求2*中间数=最小+最大;size=2时,一个数不变另一个数加/减K即可满足;size=1时,数相等。
- 其中要十分注意:是输入多组数据!!!,所以每组数据的输入时要进行set的清空!!!痛定思痛!导致爆零的罪魁祸首…
- 同时要十分注意:ai的最大值为1015,所以数据类型都要用long long!!!set和输入数据处都要定义为long long类型!
C++
#include<iostream>
#include<set>
using namespace std;
long long a1, a2, a3;
set<long long> s;
int main() {
int t;
cin >> t;
while (t--) {
s.clear();//一定要记得
int n; cin >> n;
for (int i = 0; i < n; i++) {
long long x; cin >> x;
s.insert(x);
}
int index = 1;
if (s.size() > 3) { cout << "NO" << endl; continue; }
if (s.size() == 3) {
for (set<long long>::iterator it = s.begin(); it != s.end(); it++,index++) {
if (index==1) a1 = *it;
else if (index==2) a2 = *it;
else a3 = *it;
}
if (a1 + a3 == 2 * a2)
cout << "YES" << endl;
else
cout << "NO" << endl;
}
else if(s.size()<=2)
cout << "YES" << endl;
}
return 0;
}
HRZ学英语
时间限制 | 空间限制 |
---|---|
1s | 64mb |
题目描述
瑞神今年大三了,他在寒假学会了英文的26个字母,所以他很兴奋!于是他让他的朋友TT考考他,TT想到了一个考瑞神的好问题:给定一个字符串,从里面寻找连续的26个大写字母并输出!但是转念一想,这样太便宜瑞神了,所以他加大了难度:现在给定一个字符串,字符串中包括26个大写字母和特殊字符’?’,特殊字符’?'可以代表任何一个大写字母。现在TT问你是否存在一个位置连续的且由26个大写字母组成的子串,在这个子串中每个字母出现且仅出现一次,如果存在,请输出从左侧算起的第一个出现的符合要求的子串,并且要求,如果有多组解同时符合位置最靠左,则输出字典序最小的那个解!如果不存在,输出-1! 这下HRZ蒙圈了,他刚学会26个字母,这对他来说太难了,所以他来求助你,请你帮他解决这个问题,报酬是可以帮你打守望先锋。
说明:字典序先按照第一个字母,以 A、B、C……Z 的顺序排列;如果第一个字母一样,那么比较第二个、第三个乃至后面的字母。如果比到最后两个单词不一样长(比如,SIGH 和 SIGHT),那么把短者排在前。例如
AB??EFGHIJKLMNOPQRSTUVWXYZ
ABCDEFGHIJKLMNOPQRSTUVWXYZ
ABDCEFGHIJKLMNOPQRSTUVWXYZ
上面两种填法,都可以构成26个字母,但是我们要求字典序最小,只能取前者。
注意,题目要求的是第一个出现的,字典序最小的!
输入格式
输入只有一行,一个符合题目描述的字符串。
输出格式
输出只有一行,如果存在这样的子串,请输出,否则输出-1
样例输入1
ABC??FGHIJK???OPQR?TUVWXY?
样例输出1
ABCDEFGHIJKLMNOPQRSTUVWXYZ
样例输入2
AABCDEFGHIJKLMNOPQRSTUVW??M
样例输出2
-1
数据点 | 字符串长度 |
---|---|
1,2,3 | 26 |
3,4,5 | 10000 |
6,7,8,9,10 | 106 |
分析
此题可以满足前三个数据点“骗”30分。
- 此处用到了尺取法,左右端点不断更新。从输入字符串的第一个字符开始检查,当字符为’?'时,如果取到的长度正好为26了则可更新右端点并退出了。bool chars[26]用来记录字符是否出现过了,int location[26];用来记录每个字符所在位置以便出现重复字符时更新左端点。当尺取区间长度为26时则退出(即此时必定是最靠左位置的解)。
- 注意for (int m = left; m < location[str[i] - ‘A’]; m++)循环中if (str[m] == ‘?’) continue;的添加,因为此时chars[str[m] - ‘A’]中的str[m]可能为’?’,从而导致chars数组出错!
C++
#include<string>
#include<iostream>
using namespace std;
bool chars[26] = { false };
int location[26];
int main()
{
string str;
cin >> str;
int left = 0,right = -1;
for (int i = 0; i < str.size(); i++)
{
if (str[i] == '?')
{
if ((i - left + 1) == 26)
{
right = i;
break;
}
continue;
}
if (chars[str[i] - 'A'] == true)
{
for (int m = left; m < location[str[i] - 'A']; m++)
{
if (str[m] == '?') continue;//一定要加,否则数组chars[str[m] - 'A']中会报错
chars[str[m] - 'A'] = false;//重复数第一次出现的位置之前的数全部清除
}
left = location[str[i] - 'A'] + 1; //left更新为上一次出现的后面一个位置,避免重复
location[str[i] - 'A'] = i;//更新位置
}
else
{
chars[str[i] - 'A'] = true;
location[str[i] - 'A'] = i;
}
if ((i - left + 1) == 26)
{
right = i;
break;
}
}
if (right == -1)
cout << -1;
else
{
int temp = 0;
for (int m = left; m <= right; m++)
{
if (str[m] != '?')cout << str[m];
else
{
for (int p = temp; p < 26; p++)
{
if (!chars[p])
{
cout << (char)(p + 'A');
temp = p + 1;
break;
}
}
}
}
}
return 0;
}
或者提供的:
咕咕东的奇妙序列
时间限制 | 空间限制 |
---|---|
1s | 64mb |
题目描述
咕咕东 正在上可怕的复变函数,但对于稳拿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 项数字是多少!但是她睡醒之后发现老师讲的东西已经听不懂了,因此她把这个任务交给了你。
输入格式
输入由多行组成。
第一行一个整数q表示有q组询问。(1<=q<=500)
接下来第i+1行表示第i个输入ki,表示询问第ki项数字。(1<=ki<=1018)
输出格式
输出包含q行
第i行输出对询问ki的输出结果。
样例输入
5
1
3
20
38
56
样例输出
1
2
5
2
0
数据点 | q(上限) | k(上限) |
---|---|---|
1,2,3 | 500 | 55 |
4,5,6 | 100 | 106 |
7,8,9,10 | 500 | 1018 |
分析
-
此题思路比较复杂,多次运用到了等差数组等数学知识,又用到了二分法(否则超时),断断续续两三天才完全理清楚。同时这里的数据范围为1018,所以也要用到long long类型。
-
在第1部分到第9部分,公差为1;第10到第99部分,公差为2;第100到第999部分,公差为3……如下
-------------------m = 1
//第一大段
1 第1部分 公差d=1,首项a0=1,第一段总项数n=9
1 2 由等差数列求和公式可得sum+= a0 * n + n * (n - 1)*d / 2;
1 2 3
1 2 3 4
1 2 3 4 5
……
1 2 3 4 5 6 7 8 9 结尾项为9=m-1,第9部分
-------------------m *= 10
//第二大段
1 2 3 4 5 6 7 8 9 1 0 第10部分 公差d=2,首项a0=11,第二段总项数n=90
1 2 3 4 5 6 7 8 9 1 0 1 1 由等差数列求和公式可得sum
……
1 2 3 4 5 6 7 8 9 1 0 1 1 1 2 1 3 1 4 …… 9 8 假如a为在这一部分中的第12项,则sum=10,sum++后为11,y=1,即11中从左数的第一位为答案
1 2 3 4 5 6 7 8 9 1 0 1 1 1 2 1 3 1 4 1 5 …… 9 9 结尾项为99=m-1,第99部分
-------------------
//第三大段
- 其中多次调用了getSum(x)函数,来找到此时到第x部分总的项数(x为那一行的最后一个多位数形式数)
C++
#include <cstdio>
#include <iostream>
typedef ll long long
using namespace std;
int q;
ll a;
ll getSum(ll x)
{//即找到此时到第x部分总的项数(x为那一行的最后一个多位数形式)
ll m = 1, a0 = 1, d = 0, n = 0;
ll sum = 0;
while (1)
{
m *= 10; //每一段的结尾项后一个项
d++; //公差每次增1
n = m - m / 10; //每一段的个数
if (x > m - 1)
{
sum += a0 * n + n * (n - 1)*d / 2;
a0 = a0 + (n - 1)*d + d + 1; // 下一段的首项
}
else//x <= m-1时,即x在那一大段的中间位置而不是下一大段中时
{
n = x - m / 10 + 1;//找到此时新的n值,x为那一行的尾项
sum += a0 * n + n * (n - 1)*d / 2;
break;
}
}
return sum;
}
int main()
{
scanf("%d", &q);
while (q--)
{
scanf("%lld", &a);
int left = 0, right = 1000000000, mid = 0, ans;
ll ssum;
while (left <= right)
{//二分找所在段(哪一行也即第几部分)
mid = (left + right) >> 1;
ssum = getSum(mid);
if (ssum < a)
{
ans = mid;//最终ans = (a所在那一行即那一部分的上一行的最后一个数),left = ans+1
left = mid + 1;
}
else
right = mid - 1;
}
a = a - getSum(ans);//a :在 ans+1 段的第a个数字
ll m = 1, dd = 0, n = 0,y = 0, sum = 0;//其中sum是要找的那个数字所在的多位数前一个多位数
while (a)
{//查找所在小分段(那一行即那一部分中的几位数段)
m *= 10;
n = m - m / 10;//n为多少个多位数
dd++;//dd多位数
if (a > n*dd)
{
sum += n;//此时的多位数
a -= n * dd;
}
else
{
sum += a / dd;//a所在多位数前一个多位数
y = a % dd;//y即多位数中的第y位
break;
}
}
if (y == 0) printf("%lld\n", sum % 10);//多位数的最后末尾数字
else
{
sum ++;
while (dd != y)//定位到多位数的那一位
{
dd--;
sum /= 10;
}
printf("%lld\n", sum % 10);
}
}
return 0;
}