HRZ序列
思路:
其实就是检查是否可以够成一个等差数列,只需要读入的数据中有小于等于三个数值就可以,大于三个数一定不会满足,在做这道题的时候我的出错原因是没有考虑缓冲区,每次读取了4个不同的数之后就跳出循环了,这样还有缓冲去没有读入的序列,导致错误。
整体思路判断是否等于三,等于三进行判断,sort一下再判断是否是等差数列。
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
long long int c[4];
int t;
int n;
long long int x;
int cnt;
int main()
{
cin>>t;
while(t--)
{
cin>>n;
cnt = 0;
for(int i = 0;i <n;i++){
scanf("%lld",&x);
if(cnt>3) continue;
int k = true;
for(int j = 0;j<cnt;j++)
{
if(x==c[j]) k = false;
}
if(k) c[cnt++] = x;
}
if(cnt > 3){
cout<<"NO"<<endl;
}
else if(cnt == 3){
sort(c,c+cnt);
if(c[2]-c[1]==c[1]-c[0])
{
cout<<"YES"<<endl;
}
else{
cout<<"NO"<<endl;
}
}
else if(cnt<3) cout<<"YES"<<endl;
}
}
HRZ学英语
思路
这个题更像是吃去发和滑动窗口的结合版本,首先窗口的固定大小为26,这个题首先设置一个长度为26的数组用来表示字母的访问情况,也可以直接用128大小的数组,因为ascii码的为0~127,这样可以用ASCII码访问,更方便书写,
当从A到Z的数组中所有的数量都小于等于1,则证明窗口中咩有重复且数目为26,则证明满足条件,当不满足的时候窗口后移一位,当然可以优化,在之后读入数据之后可以,如发现重复可以从重复的位置往后计算,相应的代码会复杂很多,但窗口的移动素的将加快。
代码
#include<iostream>
#include<cstring>
using namespace std;
int vis[128];
bool judge()
{
for(int i = 'A';i<='Z';i++)
if(vis[i]>1) return 0;
return 1;
}
int main()
{
string s;
cin>>s;
int l = -1;
for(int i = 0;i<26;i++)
vis[s[i]]++;
for(int i = 26;i<=s.size();i++)
{
if(judge())
{
l = i-26;
break;
}
vis[s[i-26]]--;
vis[s[i]]++;
}
if(l == -1) cout<<l<<endl;
else{
for(int i = l;i<l+26;i++)
{
if(s[i]=='?')
{
for(int j = 'A';j<='Z';j++){
if(vis[j]==0){
vis[j] = 1;
printf("%c",j);
break;
}
}
}
else{
printf("%c",s[i]);}
}
cout<<endl;
}
}
咕咕东的其妙序列
INPUT
OUTPUT
数据规模
思路
这道题一开始的思路使用模拟做,模拟的思路如下
题目中给出了
1 12 123 1234 12345 123456 1234567 12345678 123456789 1234567910
观察发现,第i个字符串和第i-1个字符串的差距是log10(i),这样我们可以后造一个前序和数组sum,第i个由第i-1个推导得到:
sum[i] = sum[i] + log10(i)
再由 sum[i] = sum[i-1] + sum[i]得到前序和
之后构造一个足够长的数
12345678910…
循环判断给定的i是否小于sum[i],如果小于
n = n - sum[i-1]
之后得到的就是该数在12345678910…中的位置,输出即可,但是这个题目的数据量达到了1e18所以这种方法只能在1e6的情况下操作。
所以有下面的方法:
通过二分不停地缩小数据范围:
1 12 123 1234 12345 123456 1234567 12345678 12345678910
我们首先统计1位2位3位等等最长的字符串的长度
91 902 9003 90004…
再计算前缀和得到当存在不出现i+1位时该字符串的最大位置,用等差数列公式和前面的前缀和即可得到。
之后进行范围缩小,首先循环遍历,看输入的位置n是包含在那个最大包括几数位的串中比如34包含在最大为1位的字符串中,假设确定的是2位则n位是
12345678910 1234567891011,
之后确定实在第几个顺串中,第i个顺串前面的数目为第i-1位的最长顺串加上i到他加上i*(i-1)的等差数列和,在这里是递增的可以使用二分法进行优化得到该数列属于第几个顺串,在用i减去顺串前面的数就得到了他在该顺串中的数值,之后再确定他位于几位数的串中,用除和取余得到位置,之后找到该数,用sstream将int变为string取得数即可
代码
#include<iostream>
#include<cmath>
#include<sstream>
using namespace std;
#define ll long long
ll sum1[11];//i位共有多少
ll sum2[11];//i位一共有多少
int wei;
ll find_wei(ll q)
{
ll w = q;
for(int i = 1; i<10; i++)
{
//如果小于i证明在第i位
if(w <= sum2[i])
{
wei = i;
w -= sum2[i-1];
return w;
}
}
wei = 10;
return w - sum2[9];
}
ll find_n(ll weizhi, int wei)
{
ll sum, k = 0, left = 1, right = 1;
for(int i = 0; i < wei; i++)
right = right*10;
right -= 1;
while(left <= right)
{
ll mid = (left + right)/2;
/*wei表示现在这一位,找到上一位的最大一位,加上现在的位数找到第一位,
再加上mid乘位数发现前面的位数*/
ll number = (sum1[wei-1] + wei + sum1[wei-1] + wei*mid)*mid/2;
if(number < weizhi)
{
left = mid +1;
k = mid;
}
else{
right = mid -1;
}
}
return (sum1[wei-1] + wei + sum1[wei - 1] + wei*k)*k/2;
}
int main()
{
sum1[0] = 0;
sum2[0] = 0;
sum1[1] = 9;
sum2[1] = 45;
for(int i = 2; i < 10; i++)
sum1[i] = sum1[i-1] + 9*i*pow(10,i-1);//9 90 900 9000
ll cntx = 10;//记录个数
for(int i = 2; i < 10; i++)
{
sum2[i] = sum2[i-1] + (i + sum1[i-1] + sum1[i])*9*cntx/2;
cntx*=10;
}
// for(long long i=1;i<10;i++)
// {
// cout<<sum2[i]<<endl;
// }
ll n;
cin>>n;
while(n--)
{
ll q;
cin>>q;
//cin>>q;
ll p = find_wei(q);
ll go = find_n(p,wei);
p -= go;
//确定是几位数
//cout<<p;
int pl = 1;
for(int i = 1; i < 10; i++)
{
//如果p小于sum[i] 证明 p是一个i位数上的某一个值
if(p <= sum1[i])
{
p -= sum1[i-1];
pl = i;
break;
}
}
ll number_of_shu;
if(p%pl == 0)
{
number_of_shu = p/pl - 1;//找到第几个数比如 100 101... pl = 3 p = 6 nfs =
p = pl;
}
else{
number_of_shu = p/pl;//7 100 101 102 +2
p = p - pl*number_of_shu;
}
ll c = pow(10,pl - 1);//1 0 2 10 3 100
c+=number_of_shu;
long long kt[15];
for(int i=pl;i>0;i--)
{
kt[i]=c%10;
c=c/10;
}
cout<<kt[p]<<endl;
}
}