A HRZ的序列
题目
相较于咕咕东,瑞神是个起早贪黑的好孩子,今天早上瑞神起得很早,刷B站时看到了一个序列aaa,他对这个序列产生了浓厚的兴趣。
他好奇是否存在一个数KKK,使得一些数加上KKK,一些数减去KKK,一些数不变,使得整个序列中所有的数相等。
其中对于序列中的每个位置上的数字,至多只能执行一次加运算或减运算或是对该位置不进行任何操作。
由于瑞神只会刷B站,所以他把这个问题交给了你!
输入
输入第一行是一个正整数 t t t表示数据组数。
接下来对于每组数据,输入的第一个正整数nnn表示序列aaa的长度,随后一行有nnn个整数,表示序列aaa
输出
输出共包含ttt行,每组数据输出一行。对于每组数据,如果存在这样的K,输出"YES",否则输出“NO”。(输出不包含引号)
样例
样例输入:
2
5
1 2 3 4 5
5
1 2 3 4 5
样例输出:
NO
NO
思路:
这个题目处的非常玄乎的感觉,但是经过分析就会发现,满足的情况要么不重合的数字数为1或2,要么是不重合的数字数是3,且这三个数等差。
因此就可以把所有的数存储进set里,自动去重,分析计算。
题目中有几个坑:
(1)根据数据范围可知用 long long 类型
(2)因为输入的是多组数据,每次使用的时候需要clear()
(3)因为数多组数据,中间判定得出的结果应该是continue,(千万不能return…)
代码:
#include<stdio.h>
#include<iostream>
#include<set>
#include<string.h>
using namespace std;
long long int a[10010];
long long int b[4];
int main()
{
int t,n;
cin>>t;
set<long long int> s;
while(t--)
{
cin>>n;
for(int i=0;i<n;i++){
cin>>a[i];
s.insert(a[i]);
}
if(s.size()<3){
printf("YES\n");
}
else if(s.size()==3){
set<long long int>::iterator it;
int i=0;
for(it=s.begin();it!=s.end();it++){
b[i]=*it;
i++;
}
if(2*b[1]==b[0]+b[2]){
printf("YES\n");
}
else printf("NO\n");
}
else {
printf("NO\n");
}
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
s.clear();
}
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个字母,但是我们要求字典序最小,只能取前者。
注意,题目要求的是 第一个出现的, 字典序最小的 !
输入
输入只有一行,一个符合题目描述的字符串。
输出
输出只有一行,如果存在这样的子串,请输出,否则输出-1
样例
样例输入1:
ABC??FGHIJK???OPQR?TUVWXY?
样例输出1:
ABCDEFGHIJKLMNOPQRSTUVWXYZ
样例输入2:
AABCDEFGHIJKLMNOPQRSTUVW??M
样例输出2:
-1
思路:
需要找到最早出现26个字母的排序,从前向后找得到的第一个满足条件的序列,输出即可。
遍历每一个字符,首先记录答案区间的左端点为0,有两种情况,问号和字母。因为问号能代替任意值,只计数即可。当出现字母的时候,需要用一个字母是否出现过的标记sign数组,用于标志字符出现几次,有两种情况,第一次出现和第二次出现。当第一次出现的时候,直接记录位置和数量;当字符出现的次数为2 的时候,就说明之前已经重复过一次,需要更新左端点。
假设出现字符数为2的字符是c,
更新的方法如下:将c 上次出现的位置(因此需要记录每一个字符上次出现的位置,用flag标记)的下一个作为答案区间的左端点,然后将这个左端点到现在位置的字符的sign 和flag 重新计数。
输出:此时有26个字符,包含问号和字母,字母是不重复的,并且要求字典序最小。因此此时将问号需要替换的字符记录在s 数组中,输出遇到问号的时候进行替换。
总结:
写的时候最主要的错误还是更新的时候考虑的不够全面。跟新很重要,很重要…
代码:
#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
string a;
int sign[26]; //标记有没有
int flag[26]; //记录最后出现的位置
int s[26];
int main()
{
cin>>a;
int start=0,i=0;
int m=0;
for( int i=0;i<a.size();i++)
{
if(a[i]-'A'!=-2){ //非问号
sign[a[i]-'A']++;
if(sign[a[i]-'A']==1){
flag[a[i]-'A']=i; //当前出现的位置
}
if(sign[a[i]-'A']==2){ //已经有了 更新位置
memset(sign,0,sizeof(sign)); //当前不可能
for(int j=flag[a[i]-'A']+1;j<=i;j++){
sign[a[j]-'A']++;
}
start=flag[a[i]-'A']+1 ; //下一个 //当不满足条件时的起始位置和相应的变化
flag[a[i]-'A']=i;
}
}
if(i-start==25)
{
//满足一个,中间可能有问号 sign<=1
int t=0;
for(int k=0;k<26;k++){
if(sign[k]==0){
s[t]=k; //记录问号的数量 和问号处应该的字母
t++;
}
}
t=0;
for( int j=start;j<=i;j++){
if(a[j]!='?'){
printf("%c",a[j]);
}
else{
char c=s[t]+'A';
printf("%c",c);
t++;
}
}
printf("\n");
return 0;
}
}
printf("-1\n");
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 项数字是多少!但是她睡醒之后发现老师讲的东西已经听不懂了,因此她把这个任务交给了你。
输入:
输入由多行组成。
第一行一个整数q表示有q组询问(1<=q<=500)(1<=q<=500)(1<=q<=500)
接下来第 i+1行表示第i个输入 ki ,表示询问第 ki项数字。(1<=ki<=1018) (1<=ki<=10^18)
输出:
输出包含 q 行
第i行输出对询问 ki 的输出结果。
样例:
样例输入:
5
1
3
20
38
56
样例输出:
1
2
5
2
0
思路:
这道题给人的感觉就是纷繁复杂,层层叠叠。
暴力,看看数据范围,别想了…
以下尝试分成下面的几个层
(1)位数段 一位数(1)、二位数(2)、三位数(3)
(2)n 位数 段
1.
1 2
1 2 3
1 2 3…n
(3) 段内,(终于这步可以稍微连续点了)
1 2 3 4 …7 8 9 1 0 1 1 … 1 0 0 1 0 1
现在的问题就变成了,从这三层一步一步的查找了,最外层二分查找是那个大段的,然后根据数量确定是到几(第二层)比如是 150 内的,然后在 150 内部查找。
注意可以提前返回的条件,比如正好是上一段的末尾。
代码:
#include<stdio.h>
using namespace std;
long long getZ(long long x)
{
long long d=0,n=0,a0=1,m=1,ans=0;//d是公差,m是每一段的结尾项-
while(1) //1-9 1, 12多两位
{
m*=10; //m-1是段的结尾组值 m=10 m=100
n=m-m/10; //n是每段的组数 n=9 n=90
d++; //当前段下的位数 d=1 d=2
if(x<=m-1) //x组在本段内 m-1 9 99
{
n=x-(m/10-1); // n=x-9=4 10 11 12 13
ans+=n*a0+n*(n-1)*d/2;//sum是前x组总共的位数 xd=
break;
}
else //x组在之后的段里 //计算之前组的位数
{
ans+=n*a0+n*(n-1)*d/2;
a0=a0+n*d+1;
}
}
return ans;
}
int main()
{
int q;
scanf("%d",&q);
while(q--)
{
long long k;
scanf("%lld",&k);
long long l=0,r=1e9,mid=0;
long long ans=0;
while(l<=r)
{
mid=(l+r)>>1;
long long t=getZ(mid);
if(k>t)
{
ans=mid;
l=mid+1;
}
else
{
r=mid-1;
}
}
long long s=k-getZ(ans);
int e;
if(s==0)
{
e=ans%10;
printf("%d\n",e);
continue;
}
long long m,n,d,sum,d1;
m=1;
n=d=sum=d1=0;
while(s)
{
m*=10;
n=m-m/10;
d++;
if(s>n*d){
sum+=n;
s=s-n*d;
}
else {
sum+=s/d;
d1=s%d;
break;
}
}
if(d1!=0){
sum++;
while(d!=d1){
sum=sum/10;
d=d-1;
}
}
e=sum%10;
printf("%d\n",e);
}
}