A HRZ的序列
题目
Input&&Output
sample
#input:
2
5
1 2 3 4 5
5
1 2 3 4 5
#output:
NO
NO
题解
1.本题我们观察发现答案未知,但是有明确边界,而且有明确移动方向因此我们想到
可以二分答案
2.注意点:数据范围,本题应使用long long;其次还应注意插值等于0时的特判
C++代码
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1e4+100;
long long t,n,ele[maxn];
void init(){
for(int i=0;i<n;i++)
ele[i] = 0;
}
int solve(){
long long mn=ele[0],mx=ele[n-1],mid=(mn+mx)/2,k=0,i=0;
while(mn<=mx){
mid = (mn+mx)/2;
k = min(mid - ele[n-1],mid - ele[0]);//k && k<=0
for(i=0;i<n;i++){
if(abs(mid-ele[i])==abs(k)||mid-ele[i]==0) continue;
if(abs(mid-ele[i])>abs(k)) mn = mid+1;
else mx = mid-1;
break;
}
if(i == n){
return 1;
}
}
return 0;
}
int main(){
cin>>t;
while(t--){
cin>>n;
init();
for(int i=0;i<n;i++) cin>>ele[i];
sort(ele,ele+n);
if(solve()) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
return 0;
}
B HRZ学英语
题目
Input&&Output
sample
#input:
ABC??FGHIJK???OPQR?TUVWXY?
#output:
ABCDEFGHIJKLMNOPQRSTUVWXYZ
题解
1.本题乍一看很向尺取,区间左右有明确的移动方向,且答案与区间内值有关
2.当我们选择尺取之后应该注意区间移动的条件以及加入踢出区间的条件,其次?的
判断尤为重要,也是最后找出最小序的关键所在,本题我才采用一个数组记录?与其
它字母的情况然后依次填补空缺
C++代码
#include<iostream>
#include<string>
using namespace std;
const int maxn = 1e6+100;
string curr;
int ele[maxn],mark[27],l=0,r=-1;
void init(){
for(int i=0;i<curr.length();i++){
if(curr[i]=='?') ele[i] = 0;
else ele[i] = curr[i] - 'A' + 1;
}
for(int i=0;i<27;i++) mark[i]=0;
}
int find(){
for(int i=1;i<27;i++){
if(mark[i]>1) return i;
}
return 0;
}
void solve(){
for(int i=0;i<curr.length();i++){
if(r-l+1<=26){
r++;
mark[ele[i]]++;
}
int m = find();
if(m!=0){
while(ele[l] != m){
mark[ele[l]]--;
l++;
}
mark[ele[l]]--;
l++;
}
//此时若 r-l+1==26 则寻找序列
int p=1;
if(r-l+1==26){
for(int j=l;j<=r;j++){
if(curr[j]=='?'){
while(mark[p]!=0) p++;
cout<<char('A'+p-1);
p++;
}
else cout<<char('A'+ele[j]-1);
}
cout<<endl;
return;
}
}
cout<<-1<<endl;
return;
}
int main(){
cin>>curr;
init();
solve();
return 0;
}
C 咕咕东的奇妙序列
题目
Input&&Output
sample
#input:
5
1
3
20
38
56
#output:
1
2
5
2
0
题解
1.本题自我感觉很神奇,至少我找了很久都没找出来规律
2.仔细分析之后发现本题实际上也是一个二分答案的过程
3.首先我看错了题以为只有 1 2 3 4 5......然后开始做题(但无意中也成了过程的
一部分,我们只观察这一部分可以发现数字占位是阶段性的(实际上是一个分段的
等差数列,因此我们按照位数划分如果给出kn就可以按照这种规律去分割,求每个
阶段的和最后加上一个不完整的阶段
4.然后发现本题实际上是上一个的重复 (1)( 1 2)( 1 2 3)
刚刚找到的规律实际上是本题的第n项的规律,因此我开始思考如何求在第n段的
kn,由于是连续数据显然暴力会超时,因此类比刚刚的这个实际上也是一个分段
的等差数列,然鹅我们却不可以知道确切的值因此我们可以采用二分答案试出来
n,然后再套用3的做法就可以正确解出题目
PS:注意范围 long long
C++代码
#include<iostream>
#include<math.h>
using namespace std;
long long maxn = 1e18+100;
long long get_number(long long kn,long long &w,long long &len){
long long begin=1,n,num=0;
len = 1;
while(true){
begin*=10;
n=begin-begin/10;
if(kn<=n*len){
num+=kn/len;
w=kn%len; //位数 作到右
return num;
}else{
num+=n;
kn-=n*len;
}
len++;
}
}
void result(long long kn){
long long w,len;
long long num = get_number(kn,w,len);
if(w!=0) num++; //防止进位
//cout<<num<<" : "<<w<<endl;
//for(;len!=w&&w!=0;len--) num/=10;
for(long long j=len-w;j>0&&w!=0;j--) num/=10;
cout<<num%10<<endl;
}
long long get_num(int n){//获取一个数所处位置 等差数列(分段) (1) (1 2) (1 2 3)……(1 2 3...n)
long long begin = 1,d = 1,sum = 0,end = 0,nu = 0,a0 = 1;//n项数 sum和 end段的最后一项 begin段起始 a0首项
while(true){
begin*=10;
end = begin-1;
if(n<=end)//本段可能未使用完
nu = n-begin/10 + 1;
else
nu = end - begin/10 + 1;
sum+=a0*nu + nu*(nu-1)*d/2;
if(n<=end) return sum;
d++;
a0 = a0 + (nu-1)*(d-1) + d;
}
}
int get_group(long long kn){//前面区间的组号
int group,l=0,r=1e9,mid;
while(l<=r){
mid = (l+r)/2;
if(get_num(mid) < kn){
l = mid+1;
group = mid;
}else{
r = mid-1;
}
}
return group;
}
int main(){
long long kn;
int q;
cin>>q;
while(q--){
cin>>kn;
int group = get_group(kn);
kn-=get_num(group);
//cout<<"group : "<<group<<" kn : "<<kn<<endl;
result(kn);
}
return 0;
}