题目一
分析
这道题并不会很难,只要稍微分析,就能发现,如果数字序列中只有1个或2个不同的数,就一定满足;有4个不同的数,就一定不满足;
唯一需要细加判断的就是3个不同的数,如果是等差数列(a[i+1]-a[i]==a[i]-a[i-1]),就满足条件。
所以步骤是
1、先为数组排序
2、用一个数组记录相互不同的数(因为>3的情况可以直接判定,那么只需要最多纪录3个数)
3、不同的数是3的情况,判断b[2]-b[1]==b[1]-b[0]是否成立。
总结
在这里,后面五个测试点是long long ,虽然俺注意到了,但是我用来记录数据的p忘记使用long long了,我的50分~~
代码
#include<stdio.h>
#include<algorithm>
using namespace std;
long long int a[10005];
int main()
{
long long int t,b[5],n;
scanf("%lld",&t);
for(int i=0;i<t;i++)
{
scanf("%lld",&n);
for(int j=0;j<n;j++)//序列个数
scanf("%lld",&a[j]);
sort(a,a+n);
long long int p=a[0];//用来记录
b[0]=p;int k=1,j=1;
while(j!=n)
{
if(p!=a[j]){
p=a[j];b[k]=p;k++;
}
if(k>3)
{
printf("NO\n");
break;
}
j++;
}
if(k==1||k==2)
printf("YES\n");
//k==3的情况
if(k==3)
if((b[1]-b[0]==b[2]-b[1]))
printf("YES\n");
else
printf("NO\n");
}
return 0;
}
题目二
分析
这道题使用尺取法,l记录符合条件的左端,r记录符合条件的右端;
每次r++的时候,判断visit[r]是否>1(也就是在这个字符段里面有重复的字母,一定不符合条件);
如果>1,那么就要将l的值移动到r所访问的字母的前面相同字母的下一个。
如果r-l+1==26,那么就找到了符合条件的字符段;
最后需要处理的是?,将缺失的字母整合起来排序,只要遇到?就用字典序最小的字母代替。
总结
在使用尺取法的时候,移动l的时候没有注意到,是移动到r所访问的字母前面与之相同字母的下一个,我直接将l移动到r前面那个字母那里了。
所以我竟然还有40分,不可思议。
代码
#include<stdio.h>
#include<string.h>
#include<map>
using namespace std;
map<char,int> visit;
void ini(){
visit['A']=0;visit['B']=0;visit['C']=0;visit['D']=0;visit['E']=0;visit['F']=0;
visit['G']=0;visit['H']=0;visit['I']=0;visit['J']=0;visit['K']=0;visit['L']=0;
visit['M']=0;visit['N']=0;visit['O']=0;visit['P']=0;visit['Q']=0;visit['R']=0;
visit['S']=0;visit['T']=0;visit['U']=0;visit['V']=0;visit['W']=0;visit['X']=0;
visit['Y']=0;visit['Z']=0;visit['?']=0;
}
int main()
{
char c[10000005];
scanf("%s",c);
int len=strlen(c);
ini();
int l=0,r=0,flag=0;
while(l<=r&&r<=len)
{
visit[c[r]]++;
if(visit[c[r]]>1&&c[r]!='?'){//有重复的,这26个字母不可能
while(l<r){
if(c[l]==c[r]) break;
visit[c[l]]--;
l++;
}
visit[c[l]]--;l++;
}
if(r-l+1==26)//符合条件
{
flag=1;break;
}
r++;
}
if(flag==0) printf("-1\n");
else
{//把?替换掉
char queshao[26];
int k=0;
//寻找缺少哪些字母
for(int i=0;i<26;i++)
{
if(visit['A'+i]==0){
queshao[k]='A'+i;k++;
}
}
k=0;
for(int i=l;i<=r;i++)
{
if(c[i]!='?') printf("%c",c[i]);
else
{
printf("%c",queshao[k]);k++;
}
}
printf("\n");
}
return 0;
}
题目三
分析
这是一道我很想暴力解决的问题,可是暴力过不了~
但是从暴力的方法中多少能借鉴点东西;
具体的想法就是,先找到x所在的组n;然后x-x前面的n-1组的和,从而更新x;然后根据x找到它在组n当中的第几个数,最后寻找x在这个数当中是第几个(位数)。
首先我暴力的求出前n-1组的和,但是好像复杂度很高;然后看别人采用两次二分去降低复杂度,就用两次二分了。
既然用到了二分的思想,就从组当中最后一个数x入手,计算该组以及前面组的总位数
将1-9;10-99;100-999…分为同一簇(同方差)
比如说x=102;那么就是在第102组当中,计算前102组的位数总和就分为两个部分,一是占用了整个簇的部分:1-9和10-99(计算前99组可以用等差数列的前n项和计算);二是占用部分簇的部分:100-102(项数=102-100+1,还是用前n项和实现)
使用二分就能不断逼向最符合的组,从而得到在哪个组。
总结
代码
#include<stdio.h>
using namespace std;
int ans[1000];
long long sum1(long long x,int flag)
{
long long int sum=0,w=0;
long long int n=0,a1=0,an=0,d=0;
while(1)
{
if(w==0) w=1;
else w=w*10;
if(x>w*10-1){
n=10*w-w;d++;a1=an+d;an=a1+(n-1)*d;sum=sum+(a1+an)*n/2;
}
else{
n=x-w+1;d++;a1=an+d;an=a1+(n-1)*d;sum=sum+(a1+an)*n/2;
break;
}
}
if(flag==1) return sum;
else return an;
}
void solve(long long x)
{
long long int p1 = 0, p2 = 0;
//erfen1
long long l=0, r=1000000000;
while(l<r-1){
long long mid = (l+r)>>1;
if(sum1(mid,1) < x){
l = mid;
}
else{
r = mid;
p1 = mid;//p1是要找的组数
}
}
x=x-sum1(p1-1,1);//在该组的第几个数
//二分找是数字几
l=0, r=p1+1;
while(l<r-1){
long long mid = (l+r)>>1;
if(sum2(mid,0) < x){
l = mid;
}
else{
p2 = mid;
r = mid;
}
}
x=x-sum2(p2-1,0);//该数字的第几个
//printf("p2: %d x:%d \n",p2,x);
/*
//第几位
int t=1,j=1;
while(t!=0){
t=(p2)/pow(10,j);j++;
}//j-1位数
int ans=((p2)%(long long int)pow(10,j-x))/pow(10,j-1-x);*/
int tot=0;
while(p2){
ans[tot] = p2%10;tot++;p2=p2/10;
}
printf("%d\n",ans[tot-x]);
}
int main(){
long long int q,k;
scanf("%lld",&q);
for(int i=0;i<q;i++){
scanf("%lld",&k);//第k个数
solve(k);
}
return 0;
}