程序设计第二次CSP模测
to sum up
模测只拿了130,超级遗憾,第一题因为少想了一种情况爆0了。感觉独自思考问题的能力以及做题经验有所欠缺。列一下模测题目。
A题
对于一个序列,判断是否存在一个数 ,使得一些数加上 ,一些数减去 ,一些数不变,使得整个序列中所有的数相等,其中对于序列中的每个位置上的数字,至多只能执行一次加运算或减运算或是对该位置不进行任何操作。
Sample Input and Output
Input
输入第一行是一个正整数 表示数据组数。 接下来对于每组数据,输入的第一个正整数 表示序列 的长度,随后一行有 个整数,表示序列 。
2
5
1 2 3 4 5
5
1 2 3 4 5
Output
输出共包含 行,每组数据输出一行。对于每组数据,如果存在这样的K,输出"YES",否则输出“NO”。(输出不包含引号)
NO
NO
需要注意的就是数据范围:
需要开long long
解题思路及代码
#include <iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
#include<stack>
using namespace std;
const int MAX= 1e5;
int t,n;
long long a[MAX];
long long T[MAX];
int num;
void whe(long long u)
{
for(int i=0;i<num;i++)
{
if(u==T[i])
return;
}
T[num]=u;
num++;
}
int main()
{
cin>>t;
for(int i=0;i<t;i++)
{
num=0;
cin>>n;
for(int j=0;j<n;j++)
{
cin>>a[j];
whe(a[j]);
}
if(num<3)
cout<<"YES";
else if(num==3)
{
long long Max=max(max(T[0],T[1]),T[2]);
long long MIN=min(min(T[0],T[1]),T[2]);
if((Max+MIN)==2*T[0] || (Max+MIN)==2*T[1] || (Max+MIN)==2*T[2])
cout<<"YES";
else
cout<<"NO";
}
else
cout<<"NO";
if(i!=t-1)
cout<<endl;
}
return 0;
}
总结:
好不容易躲过了数据范围的坑,却没考虑到序列的值可以都一样。
思路很简单,主要是计数,如果序列只有两个值或者都一样,一定可以。如果有三种值,需要计算这三个值是否形成等差数列。大于三个值则一定不可以。
B题
现在给定一个字符串,字符串中包括26个大写字母和特殊字符’?’,特殊字符’?'可以代表任何一个大写字母。现在TT问你是否存在一个位置连续的且由26个大写字
母组成的子串,在这个子串中每个字母出现且仅出现一次,如果存在,请输出从左侧算起的第一个出现的符合要求的子串,并且要求,如果有多组解同时符合位置最靠左,则输出字典序最小的那个解!如果不存在,输出-1!
说明:字典序 先按照第一个字母,以 A、B、C……Z 的顺序排列;如果第一个字母一样,那么比较第二个、第三个乃至后面的字母。如果比到最后两个单词不一样长(比如,SIGH 和 SIGHT),那么把短者排在前。
Sample Input and Output
Input
输入只有一行,一个符合题目描述的字符串。输
ABC??FGHIJK???OPQR?TUVWXY?
Output
输出只有一行,如果存在这样的子串,请输出,否则输出-1
ABCDEFGHIJKLMNOPQRSTUVWXYZ
整体思路及代码:
主要用尺取法,先选定前26个字母,统计每个字母的个数和 ?的个数。需要观察两个值,一个是个数为0的字母的数量,另一个是 ? 的个数。如果两个值相等,则满足条件。如果不满足,两个边界l和r同时有益,更新数量值。当满足条件时,从26个字母数组中,将数组值为0的字母按序输出。
#include <iostream>
#include<string>
#include<algorithm>
using namespace std;
int l,r;
string list;
int num1,num2;
int a[26];
void init()
{
for(int i=0;i<26;i++)
a[i]=0;
num1=0;
num2=0;
for(int i=0;i<26;i++)
{
if(list[i]=='?')
num2++;
else
a[list[i]-65]++;
}
for(int i=0;i<26l;i++)
if(a[i]==0)
{
num1++;
}
}
void move()
{
if(list[l]=='?')
num2--;
else
{
a[list[l]-65]--;
if(a[list[l]-65]==0)
num1++;
}
l++,r++;
if(list[r]=='?')
num2++;
else{
if(a[list[r]-65]==0)
num1--;
a[list[r] - 65]++;
}
}
int main()
{
cin>>list;
init();
int n=list.size();
l=0, r=25;
while(num1!=num2)
{
if(r==n-1)
break;
move();
}
//输出
int tar=0;
if(r==n-1 && num1!=num2)
{
cout << -1;
return 0;
} else{
for(int i=0;i<26;i++)
{
if (list[l + i] == '?')
{
for(int k=tar;k<26;k++)
if(a[k]==0)
{
cout<<(char)(k+65);
tar=k+1;
break;
}
} else
cout<<list[l + i];
}
}
return 0;
}
C题 奇妙的序列
自然增长序列1 12 123 1234 12345… 需要查询该序列第n项的值并输出。
Sample Input and Output
Input
输入由多行组成。
第一行一个整数q表示有q组询问(q<500)
接下来第i+1行表示第i个输入 ,表示询问第ki项数字。(k<1e18)
5
1
3
20
38
56
Output
输出包含q行。第i行输出对询问ki的输出结果。
1
2
5
2
0
解题思路及代码
整个过程分为三步,第一步是将原自然增长序串拆分,从1到第一次出现k定为第k个子串。然后用二分法确定输入的数w在第几个子串中,(利用等差数列求前n项和的方法)。最后确定w在子串中具体属于哪一个数字的哪一位。
#include<iostream>
#include<vector>
using namespace std;
const long long N = 1e9;
long long l,r,mid,ans;
//二分法确定子序列
long long getlist(long long x)
{
long long pwr=10,Nsum=0,sum=0,n=0,d=1;
while(x>pwr-1)
{
n=pwr-pwr/10;
Nsum+=(sum+d)*n+n*(n-1)/2*d;
sum+=n*d;
pwr*=10;
d++;
}
n=x-pwr/10+1;
Nsum+=(sum+d)*n+n*(n-1)/2*d;
return Nsum;
}
long long speci(long long x)
{
l=0;
r=N;
while(l<=r)
{
mid=(l+r)/2;
if(getlist(mid)<x)
ans=mid,l=mid+1;
else r=mid-1;
}
x-=getlist(ans);
return x;
}
//二分法确定n在子序列中的位置
long long findNthDigit(long long p)
{
long long tmp=speci(p);
//cout<<'!'<<endl;
long long m=1,len=0,n=0,y=0,sum=0;
while(tmp)
{
m*=10;
n=m-m/10;
len++;
if(tmp>n*len)
{
sum+=n;
tmp-=n*len;
} else{
sum=sum+tmp/len;
y=tmp%len;
break;
}
}
if(y==0)
return sum%10;
else
{
sum++;
while(len!=y)
{
len--;
sum/=10;
}
return sum%10;
}
}
long long n,k;
int main()
{
cin>>n;
//init();
for(int i=0;i<n;i++)
{
cin>>k;
//cout<<'!'<<endl;
cout<<findNthDigit(k)<<endl;
}
return 0;
}