T1 HRZ的序列
问题描述
相较于咕咕东,瑞神是个起早贪黑的好孩子,今天早上瑞神起得很早,刷B站时看到了一个序列aa,他对这个序列产生了浓厚的兴趣。
他好奇是否存在一个数KK,使得一些数加上KK,一些数减去KK,一些数不变,使得整个序列中所有的数相等。
其中对于序列中的每个位置上的数字,至多只能执行一次加运算或减运算或是对该位置不进行任何操作。
由于瑞神只会刷B站,所以他把这个问题交给了你!
输入格式
输入第一行是一个正整数tt表示数据组数。
接下来对于每组数据,输入的第一个正整数nn表示序列aa的长度,随后一行有nn个整数,表示序列aa。
输出格式
输出共包含tt行,每组数据输出一行。对于每组数据,如果存在这样的K,输出"YES",否则输出“NO”。(输出不包含引号)
样例输入
2
5
1 2 3 4 5
5
1 2 3 4 5
样例输出
NO
NO
解题思路
- 符合题意的数只能最多只能有三种,即a-k,a,a+k;多一种则不满足
- 通过对输入的数组去重来计算种类。
- 种类数<3,则一定YES;
- 种类数>3,则一定NO;
- 种类数=3,则判断是否为a-k,a,a+k;是则YES,否则NO;
完整代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
long long a[100010];
long long aa[100010];
//去重后数目大于3则不可能
int n,t,Count;
int main()
{
int t;
cin>>t;
while(t--)
{ Count=1;
cin>>n;
for(int i=0; i<n; i++)
cin>>a[i];
sort(a,a+n);//排序后去重
aa[0]=a[0];//第一个一定在里面
int l=0,r=1;
while(l<n)
{
if(a[l]!=a[r])//找到新的不等于的数
{
l=r;//l移到当前位置,记录新数
aa[Count]=a[l];
Count++;
}
r++;
}
if(Count<4||(Count==4&&aa[0]+aa[2]==2*aa[1]))
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
}
return 0;
}
T2 HRZ学英语
问题描述
瑞神今年大三了,他在寒假学会了英文的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
解题思路
- 采用尺取法,长度不到26则右指针右移;到了则break;
- 每次判断end处元素:
- 是’?'则右移,长度++;
- 是没到过的数则标记为到过,长度++,右移;
- 是到过的数,则从左指针到当前位置,所有数依次标记为未到达(除问号)、长度–,
- 最后判断长度是否为26;是则输出当前序列,遇到问号则从未到达的字母中从小到大取出一个并标记为到达;否则输出-1;
完整代码
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
string a;
int reach[30];
int main()
{
for(int i=0;i<27;i++)
reach[i]=0;
int qposition=0;
long long begin=0;
long long end=0;
long long length=0;
cin>>a;
long long len=a.size();
int in;
while(end<len)
{
if(a[end]=='?')
{
end++;//后移
length++;//长度++
}
else//不是问号
{
in=a[end++]-'A';
if(reach[in]==0)
{
reach[in]=1;
length++;
}
else//右边找到了到过的,把begin弄到左边第一个的后面
{int m;
m = a[begin++]-'A';
while(m!=in){
if(a[begin-1]!='?')
reach[m] = 0;
length--;
m = a[begin++]-'A';
}}}
/* while(a[begin]!=a[end])
{if(a[begin]!='?')
{ m=a[begin]-'A';
reach[m]=0;}
begin++;
length--;}
}end++;begin++;
*/
if(length==26)
break;
}
if(length==26)
{
for(int i=begin;i<end;i++)
{if(a[i]=='?')
{ while(reach[qposition]!=0)
qposition++;
cout<<char(qposition+'A');
reach[qposition]=1;
continue;}
cout<<a[i];
}
cout<<endl;
return 0;}
cout<<"-1"<<endl;
}
T3 咕咕东的奇妙序列
问题描述
咕咕东 正在上可怕的复变函数,但对于稳拿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)(1<=q<=500)
接下来第i+1行表示第i个输入k_i,表示询问第k_i
项数字。(1<=k_i<=10^{18})(1<=k i<=10 ^18 )
输出格式
输出包含q行
第i行输出对询问k_ik i的输出结果。
样例输入
5
1
3
20
38
56
样例输出
1
2
5
2
0
解题思路
- 判断第k位对应的数字,可以从大到小,先判断k是几位数,再二分判断k属于哪一块,再判断k在该块中对应第几个数,再判断k在该数中是第几位
- a数组表示 最多表示i位数的 能表示数字的最大位数和,sum1[i]=sum1[i-1]+9*10^(i-1)*i;
- b数组表示最多表示i位数的数位总数,sum2[i]=sum2[i-1]+等差数列的和(等差数列的首项为sum1[i-1]+i,公差为i,项数为9*10^(i-1))
- start数组表示第i位数的首项
- 判断几位数,即与b[i]进行比较。找到最后一个小于k的b[i],k减去它可以得到在当前大组中的位数,同时得到该数据所处大组中的上下界。
- 有了上下界,可以二分搜索k的具体块,再找到k在区间中的位置,判断位数即可。
完整代码
#include <iostream>
using namespace std;
long long a[10];//最多i位数 能表示的最多位数 ,a[1]=9,a[2]=90*1+90*2=189......
long long b[10];//最多表示i位数 位数的和,b[1]=45,b[2]=1+2+...+9+11+13+...=9045
long long start[10];
long long n;
int main()
{
int q;
cin>>q;
start[0]=1;
n=9;
for(int i=1;i<=9;i++)
{
start[i]=start[i-1]*10;//每行第一个数的索引
a[i]=a[i-1]+(n*start[i-1]*i);//i是位数
}
long long a1;
int d;
for(int i=1;i<=9;i++)
{
a1=a[i-1]+i;//a1是首项,i表示位数
d=i;
b[i]=b[i-1]+((a1*n)+((n*(n-1)*d)/2));
n=n*10;
}
while(q--)
{
long long k;
cin>>k;
int i=1;
for(;k>b[i];i++) //找到k的位数
{}
k=k-b[i-1];//k在该区间内的位置
//二分查找
long long l=start[i-1];
long long r=start[i]-1;
long long ans=1;
long long temp1=l;
a1=a[i-1]+i;
d=i;
while(l<=r)//二分查找
{
long long mid=(l+r)>>1;
n=mid-temp1+1;
if(a1*n+((n*(n-1)*d)/2)<k)
{
l=mid+1;
}
else
{
r=mid-1;
ans=mid;
}
}
n=ans-start[i-1];//该行在该区间的位置
k=k-((a1*n)+((n*(n-1)*d)/2));//k在单个区间的位置
int j;
n=9;
long long temp2;
for(j=1;j<=9;j++)
{
temp2=n*j;
n=n*10;
if(k<=temp2)
{
break;
}
k=k-temp2;
}
//判断在数字内的位置
if(k%j==0)//是整数
{
long long m=(start[j-1]-1+(k/j))%10;
cout<<m<<endl;
}
else //查找属于十进制下的哪一个数
{
long long r=start[j-1]+(k/j);
for(int p=j-(k%j);p>0;p--)
r=r/10;
long long m=r%10;
cout<<m<<endl;
}
}
}