注:所有题目限制均为1s,64MB
1-HRZ的序列
相较于咕咕东,瑞神是个起早贪黑的好孩子,今天早上瑞神起得很早,刷B站时看到了一个序列 ,他对
这个序列产生了浓厚的兴趣,他好奇是否存在一个数 ,使得一些数加上 ,一些数减去 ,一些数不
变,使得整个序列中所有的数相等,其中对于序列中的每个位置上的数字,至多只能执行一次加运算或
减运算或是对该位置不进行任何操作。由于瑞神只会刷B站,所以他把这个问题交给了你!
输入格式
输入第一行是一个正整数 表示数据组数。 接下来对于每组数据,输入的第一个正整数 表示序列 的长
度,随后一行有 个整数,表示序列a 。
输出格式
输出共包含 行,每组数据输出一行。对于每组数据,如果存在这样的K,输出"YES",否则输出“NO”。(输出不包含引号)
输入样例
25
1 2 3 4 5
5
1 2 3 4 5
输出样例
NO
NO
数据说明
数据点(上限) | t t t | n n n | a i a_i ai |
---|---|---|---|
1,2 | 10 | 10 | 10 |
3,4,5 | 10 | 1 0 3 10^3 103 | 1 0 9 10^9 109 |
6,7,8,9,10 | 10 | 1 0 4 10^4 104 | 1 0 15 10 ^ {15} 1015 |
易错点
- 数据范围是
1
0
15
10^{15}
1015,需要使用
long long
表示
思路分析
- 将所有元素排序。
- 如果序列中只有一种或两种不同的数则输出YES
- 如果有三种不同的数且他们的差相等,则输出YES
代码
#include<iostream>
#include<cstdio>
#include<queue>
#include<algorithm>
#include<vector>
#include<cstring>
#include<string>
using namespace std;
int t,n,ans,dif;
long long a[10005];
long long temp,l,r,m;
int main(){
cin>>t;
while(t--){
ans=0;dif=1;
cin>>n;
for(int i=0;i<n;i++){
cin>>a[i];
}
sort(a,a+n);
temp=a[0],l=a[0],r=a[n-1],m=a[n-1];
//1、所有数字都相等
if(a[0]==a[n-1]){
cout<<"YES"<<endl;
continue;
}
for(int i=1;i<n;i++){
if(a[i]!=temp){
if(dif==1){
m=a[i];
}
else if(dif==2){
r=a[i];
}
dif++;
temp=a[i];
}
}
//2、只有两种数
if(dif==2){
cout<<"YES"<<endl;
continue;
}
//3、三种不同的数
else if(dif==3&&r-m==m-l){
cout<<"YES"<<endl;
continue;
}
cout<<"NO"<<endl;
}
return 0;
}
2-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?
样例输出
ABCDEFGHIJKLMNOPQRSTUVWXYZ
数据说明
数据点 | 字符串长度 |
---|---|
1,2,3 | 26 |
4,5,6 | 1 0 4 10^4 104 |
7,8,9,10 | 1 0 6 10^6 106 |
错题点
当右指针指向的元素已经出现时,左指针该如何变化。
思路分析
设左右指针l和r,r为工作指针,从左到右扫描字符串。
- 如果l和r取的范围>26,左指针左移,并处理丢弃的元素。
- 如果r对应
?
,问号数++ - 如果r对应的字母已经出现,左指针向左移动到出现r对应的字母的下一个位置,并处理丢弃的元素
- 如果r对应的字母没有已经出现,标记已经出现。
- 最后判断是否满足条件
#include<iostream>
#include<cstdio>
#include<queue>
#include<algorithm>
#include<vector>
#include<cstring>
#include<string>
using namespace std;
string s;
int l=0,r=0;
int alp[26],numofw,ans;
int main(){
cin>>s;
while(r<s.length()){
//1、是否超界
if(r-l>25){
if(s[l]=='?'){
numofw--;
}
else{
alp[s[l]-'A']=0;
}
l++;
}
//2、处理r
if(s[r]=='?'){//问号
numofw++;
}
else if(alp[s[r]-'A']==1){//已经有了这个字母了 bug点!!
while(s[l]!=s[r]){
if(s[l]=='?'){
numofw--;
}
else{
alp[s[l]-'A']=0;
}
l++;
}
l++;
}
else {//这个字母还没有
alp[s[r]-'A']=1;
}
//3、判断是否满足条件
if(r-l==25){
int nothave=0;
for(int i=0;i<=25;i++){
if(alp[i]==0) nothave++;
}
if(nothave==numofw){
ans=1;
break;
}
}
//4、r++
r++;
}
if(ans==1){//输出函数
for(int i=l;i<=r;i++){
if(s[i]=='?'){
for(int j=0;j<=25;j++){
if(alp[j]==0){
s[i]='A'+j;alp[j]=1;
break;
}
}
}
cout<<s[i];
}
cout<<endl;
}
else cout<<-1<<endl;
return 0;
}
3-咕咕东的奇妙序列【二分】
咕咕东 正在上可怕的复变函数,但对于稳拿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组询问
接下来第i+1行表示第i个输入
k
i
k_i
ki ,表示询问第
k
i
k_i
ki项数字。
输出格式
输出包含q行 第i行输出对询问 k i k_i ki的输出结果。
样例输出
5
1
3
20
38
56
样例输出
1
2
5
2
0
数据说明
数据点 | q | k |
---|---|---|
1,2,3 | 500 | 55 |
4,5,6 | 100 100 100 | 1 0 6 10^6 106 |
7,8,9,10 | 500 500 500 | 1 0 18 10^{18} 1018 |
理论分析
- 设 s s u m [ x ] ssum[x] ssum[x]为序列从开头到第一次出现x的总长度, s u m [ x ] sum[x] sum[x]为1~x的总长度,那么有关系 ∑ k = 1 x s u m [ k ] = s s u m [ x ] \sum_{k=1}^xsum[k]=ssum[x] ∑k=1xsum[k]=ssum[x]。
- 在 [ 1 0 k , 1 0 k + 1 ) [10^k,10^{k+1}) [10k,10k+1)范围内的每一个数的长度都是 k + 1 k+1 k+1。所以 s u m [ x ] , x ∈ [ 1 0 k , 1 0 k + 1 ) sum[x],x \in [10^k,10^{k+1}) sum[x],x∈[10k,10k+1)构成了等差数列,公差为 d = k + 1 d=k+1 d=k+1。
- s s u m [ x ] ssum[x] ssum[x]的计算方法:利用等差数列求和公式 S n = n a 1 + n ( n − 1 ) / 2 ∗ d S_n=na_1+n(n-1)/2*d Sn=na1+n(n−1)/2∗d.
- s u m [ x ] sum[x] sum[x]的计算方法:利用等差数列的通项公式 a n = a p + ( n − p ) d a_n=a_p+(n-p)d an=ap+(n−p)d
实现分析
1、对于一个数x,计算
s
s
u
m
[
x
]
ssum[x]
ssum[x]和
s
u
m
[
x
]
sum[x]
sum[x]采用
O
(
l
o
g
n
)
O(logn)
O(logn)的算法。将x分成1位数,2位数,…,n位数来计算,即1-9,10-99,100-999,…。每一次按照等差数列的公式计算。
2、二分求解
s
s
u
m
[
x
]
<
k
ssum[x]<k
ssum[x]<k的最大的x。那么答案在总序列中位于连续从1到x+1的的那一段。
k
−
=
s
s
u
m
[
x
]
k-=ssum[x]
k−=ssum[x]
3、二分求解
s
u
m
[
x
]
<
k
sum[x]<k
sum[x]<k的最大的x。
k
−
=
s
u
m
[
x
]
k-=sum[x]
k−=sum[x]那么答案是数x+1第k位。
代码
#include<iostream>
#include<cstdio>
using namespace std;
long long k,l,r,mid,ans;
int q;
long long get(long long x,int opt){
long long pwr=1,ssum=0,sum=0,n=0,d=0;
while(true){
pwr*=10;d++;
if(x>pwr-1){
n=pwr-pwr/10;
ssum+=(sum+d)*n+n*(n-1)/2*d;
sum+=n*d;
}
else{
n=x-pwr/10+1;
ssum+=(sum+d)*n+n*(n-1)/2*d;
sum+=n*d;
break;
}
}
return opt==1?ssum:sum;
}
int main(){
cin>>q;
while(q--){
cin>>k;
l=0;r=1e9;
while(l<=r){//寻找<k的最大的
mid=(l+r)/2;
if(get(mid,1)<k) ans=mid,l=mid+1 ;
else r=mid-1;
}
k-=get(ans,1);
l=0;r=ans+1;
while(l<=r){//寻找<k最大的
mid=(l+r)/2;
if(get(mid,2)<k) ans=mid,l=mid+1;
else r=mid-1;
}
k-=get(ans,2);
//第ans+1个数的第k位
ans++;
string s=to_string(ans);
printf("%d\n",s[k-1]-'0');
}
}