一、A - HRZ 的序列
题目
相较于咕咕东,瑞神是个起早贪黑的好孩子,今天早上瑞神起得很早,刷B站时看到了一个序列aaa,他对这个序列产生了浓厚的兴趣。
他好奇是否存在一个数KKK,使得一些数加上KKK,一些数减去KKK,一些数不变,使得整个序列中所有的数相等。
其中对于序列中的每个位置上的数字,至多只能执行一次加运算或减运算或是对该位置不进行任何操作。
由于瑞神只会刷B站,所以他把这个问题交给了你!
输入格式
输入第一行是一个正整数ttt表示数据组数。
接下来对于每组数据,输入的第一个正整数nnn表示序列aaa的长度,随后一行有nnn个整数,表示序列aaa。
输出格式
输出共包含ttt行,每组数据输出一行。对于每组数据,如果存在这样的K,输出"YES",否则输出“NO”。(输出不包含引号)
sample
2
5
1 2 3 4 5
5
1 2 3 4 5
NO
NO
解题思路
对于序列中的一个数,若要进行加一个值、减一个值、保持不变其中的一个操作,使变换后的序列值相等,就要找到这个序列的最小值和最大值,两者中间的数就是最终序列值。由此k易求得。遍历序列,若每个数字加上k或者减去k与最终值不等,那么这个序列就不能符合要求。
同时仍需考虑一种情况,也就是训练中的值去重复后只有两个数字。
这样求最终数的时候k为浮点数,但是代码中定义的是long long(若改成float double,则在极大数的时候会出现错误),会丢失精度,所以在这里我们设计一个set。将序列中的值加入set去重复,若set的size只有2,那么就一定可以形成题目要求的序列,直接输出yes就好。
代码
#include<stdio.h>
#include<iostream>
#include<set>
#include<math.h>
using namespace std;
long long a[10010];
set<long long> thisS;
int main()
{
int t,n;
long long mina,maxa;
scanf("%d",&t);
while(t--)
{
thisS.clear();
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%lld",&a[i]);
thisS.insert(a[i]);
}
if(thisS.size()==2||thisS.size()==1)
{
printf("YES\n");
continue;
}
mina=a[0],maxa=a[0];
for(int i=0;i<n;i++)
{
if(a[i]>maxa)maxa=a[i];
if(a[i]<mina)mina=a[i];
}
long long k=(maxa-mina)/2;
long long mid=(maxa+mina)/2;
int flag=0;
for(int i=0;i<n;i++)
{
if(a[i]==mid)continue;
else if(a[i]>mid)
{
if(a[i]-mid!=k)
{
flag=1;//
break;
}
}
else if(a[i]<mid)
{
if(mid-a[i]!=k)
{
flag=1;
break;
}
}
}
if(flag==1)
printf("NO\n");
else if(flag==0)
printf("YES\n");
}
return 0;
}
2.B - HRZ 学英语
瑞神今年大三了,他在寒假学会了英文的26个字母,所以他很兴奋!
于是他让他的朋友TT考考他,TT想到了一个考瑞神的好问题:给定一个字符串,从里面寻找 连续的26个大写字母 并输出!
但是转念一想,这样太便宜瑞神了,所以他加大了难度:现在给定一个字符串,字符串中包括26个大写字母和特殊字符’?’,特殊字符’?'可以代表任何一个大写字母。
现在TT问你是否存在一个 位置连续的且由26个大写字母组成的子串 ,在这个子串中每个字母出现且仅出现一次,如果存在,请输出从左侧算起的第一个出现的符合要求的子串,并且要求,如果有多组解同时符合位置最靠左,则输出字典序最小的那个解!如果不存在,输出-1!
这下HRZ蒙圈了,他刚学会26个字母,这对他来说太难了,所以他来求助你,请你帮他解决这个问题,报酬是可以帮你打守望先锋。
说明:字典序 先按照第一个字母,以 A、B、C……Z 的顺序排列;如果第一个字母一样,那么比较第二个、第三个乃至后面的字母。如果比到最后两个单词不一样长(比如,SIGH 和 SIGHT),那么把短者排在前。例如
AB??EFGHIJKLMNOPQRSTUVWXYZ
ABCDEFGHIJKLMNOPQRSTUVWXYZ
ABDCEFGHIJKLMNOPQRSTUVWXYZ
上面两种填法,都可以构成26个字母,但是我们要求字典序最小,只能取前者。
注意,题目要求的是 第一个出现的, 字典序最小的 !
格式
输入只有一行,一个符合题目描述的字符串。
输出格式
输出只有一行,如果存在这样的子串,请输出,否则输出-1
sample
ABC??FGHIJK???OPQR?TUVWXY?
ABCDEFGHIJKLMNOPQRSTUVWXYZ
AABCDEFGHIJKLMNOPQRSTUVW??M
-1
解题思路
若一个26字母的子串符合要求的话,那么字符串中’?'的个数一定是出现频率为零的字母的个数。
所以设立一个map,存储每个字母和它在该子串中出现的频数。
首先从序列中读入前26个字符。统计问号的个数和出现频率为零的字母的个数。当没有循环到序列的末尾时,判断目前的子串是否符合要求,当不符合要求时,将子串右移一个单位。及l++,r++。因为l-1位置的字符已经移出去,所以要将该字符的频率-1。同理移进来的字符频率+1。
当该子串符合要求时,从左向右遍历这26个字符,如果不是’?’,直接输出,若是,遍历map。若mao的频率值为零,输出该字符。注意当遇到下一个问号需要输出时,map遍历的起始点应该是上一次遍历到的末尾点加一。
注意:数组开大亿点点
代码
#include<iostream>
#include<stdio.h>
#include<map>
#include<string.h>
using namespace std;
char c[10000000];
map<int,int> map1;
int main()
{
//char cc;
cin>>c;
int l=0,r=25;
int num[3]={0};
int count1=0,count2=0;//?个数、未出现的字母个数
for(int i=0;i<26;i++)
{
if(c[i]=='?')
count1++;
map1[c[i]-65]++;
}
for(int i=0;i<26;i++)
if(map1[i]==0)count2++;
//如果26个字符中?的个数不为出现频率为0的字母个数,则子串不符合
int siz=strlen(c);
while(r<siz)
{
if(count1!=count2)//该子串不符合要求
{
l+=1;r+=1;
if(r==siz)break;
if(c[l-1]=='?')count1--;
else if(c[l-1]!='?')
{
map1[c[l-1]-65]--;
if(map1[c[l-1]-65]==0)count2++;
}
if(c[r]=='?')count1++;
else if(c[r]!='?')
{
map1[c[r]-65]++;
if(map1[c[r]-65]==1)count2--;
}
}
else//符合要求
{
int p=0;
for(int i=l;i<=r;i++)
{
if(c[i]!='?')cout<<c[i];
else
{
for(int j=p;j<26;j++)
{
if(map1[j]==0)
{
cout<<char(j+65);
p=j+1;
break;
}
}
}
}
exit(0);
}
}
cout<<-1;
return 0;
}
3.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个输入kik_iki,表示询问第kik_iki项数字。(1<=ki<=1018)(1<=k_i<=10^{18})(1<=ki<=1018)
输出格式
输出包含q行
第i行输出对询问kik_iki的输出结果。
5
1
3
20
38
56
1
2
5
2
0
解题思路
将整个序列拆分成很多层。每一层由1~i的数字组成。下一层与上一层的数字个数关系是sum[i]=sum[i-1]+log10(i)+1。
对于一个n来说,首先要找到它是属于哪一层,然后找到这一层中它属于几位数的部分,找到属于几位数的部分之后找到他是在哪一个数中,然后找到它是这个数中的第几位,然后输出该数字就好。
但是这种算法,第对于极大数不适合,78910数据点不可以ac。
代码
#include<iostream>
#include<string>
#include<algorithm>
#include<stdio.h>
#include<math.h>
using namespace std;
const int maxc=1e6;
long long sum[maxc]={0};
int main()
{
//计算第i层的数字个数
for(int i=1;i<maxc;i++)
sum[i]=sum[i-1]+log10(i)+1;
int q;
long long k;
scanf("%d",&q);
while(q--)
{
scanf("%lld",&k);
//计算K在第几层
long long i=0;
while(i<maxc)
{
if(k>sum[i])
{
k-=sum[i];i++;
}
else break;
}
//在第i层
//计算k是在几位数的部分
long long t1=1,t2=1,t3=0;//第t1部分
while(k>t3)
{
t3=t3+t1*9*t2;
t1+=1;
t2*=10;
}
t1--;//属于t1位数的部分
t2/=10;
t3-=t1*9*t2;
long long d=k-t3;
long long theNum=t2+(d-1)/t1;
long long res=(d-1)%t1; //string s=to_string(theNum);
char s[100];
sprintf(s,"%lld",theNum);
cout<<s[res]-'0'<<endl;
}
return 0;
}