A - HRZ的序列
题意
现有一个长度为n的序列,问是否存在这样一个数,使得序列中一些数加上这个数 ,一些数减去这个数,一些数不变,使得整个序列中所有的数相等(其中对于序列中的每个位置上的数字,至多只能执行一次加运算或减运算或是对该位置不进行任何操作)。
Input
输入第一行是一个正整数 t, 表示数据组数。接下来对于每组数据,输入的第一个正整数n表示a序列的长度,随后一行有n个整数,表示a序列 。
ai<e15
Output
输出共包含t行,每组数据输出一行。对于每组数据,如果存在这样的K,输出"YES",否则输出“NO”。(输出不包含引号)
思路
首先数字的重复个数这个是没有必要在意的,我们可以只看数字的大小,设输入a,b,c,d等便于表述。我们可以看出输出YES只有以下几种情况:
(1)a
(2)a,b
(3)a,b,c 且 a>b>c,a+c=2*b
只有在这三种情况下我们才能输出YES,其他一律为NO。
注意
本题的数据范围很大,要用long long,但是本人很傻的用了long long,读入scanf里却用了“%d”,一定要注意这些细节。
代码
#include<iostream>
#include<cstdio>
using namespace std;
long long a[110];
void run()
{
int n,tot,k;
long long x;
cin>>n;
tot=0;
for (int i=0;i<=3;i++)
a[i]=0;
for (int i=0;i<n;i++)
{
scanf("%lld",&x);
if (tot<=3)
{
k=-1;
for (int j=0;j<tot;j++)
if (a[j]==x)
k=j;
if (k==-1)
{
a[tot]=x;
tot++;
}
}
}
if (tot>3)
{
cout<<"NO"<<endl;
return;
}
if (tot<=2)
{
cout<<"YES"<<endl;
return;
}
x=a[0]+a[1]+a[2];
for (int i=0;i<3;i++)
if (a[i]*3==x)
{
cout<<"YES"<<endl;
return;
}
cout<<"NO"<<endl;
return;
}
int main()
{
int t;
cin>>t;
for (int ll=0;ll<t;ll++)
run();
return 0;
}
第二题
题意
现在给定一个字符串,字符串中包括26个大写字母和特殊字符 ‘?’ ,特殊字符 ‘?’ 可以代表任何一个大写字母。问是否存在一个位置连续的且由26个大写字母组成的子串,在这个子串中每个字母出现且仅出现一次,如果存在,请输出从左侧算起的第一个出现的符合要求的子串,并且要求,如果有多组解同时符合位置最靠左,则输出字典序最小的那个解!如果不存在,就输出-1!
说明:字典序 先按照第一个字母,以 A、B、C……Z 的顺序排列;如果第一个字母一样,那么比较第二个、第三个乃至后面的字母。如果比到最后两个单词不一样长(比如,SIGH 和 SIGHT),那么把短者排在前。例如
AB??EFGHIJKLMNOPQRSTUVWXYZ
ABCDEFGHIJKLMNOPQRSTUVWXYZ
ABDCEFGHIJKLMNOPQRSTUVWXYZ
上面两种填法,都可以构成26个字母,但是我们要求字典序最小,只能取前者。
注意,题目要求的是 第一个出现的,字典序最小的!
Input
输入只有一行,一个符合题目描述的字符串。
Output
输出只有一行,如果存在这样的子串,请输出,否则输出-1
思路
我们直接开一个数组a,a[i]表示第i个字母在s[l]到s[r]中出现的次数。
r右移一位,a[s[r]-‘A’]++,同时如a[s[r]-‘A’]==2,则l++直至a[s[r]-‘A’]1,这样就保证了[l,r]中绝对不会有两个相同的字母。当r-l25时,对该字符串中的?进行填充输出就可以了。
注意
本人再次傻傻的求了所有可能中的最小字典序的字符串,而忽略了第一个出现这个重要因素,CSP赛制下一定要对题目的阅读以及自己的代码慎之又慎,一锤子买卖。
代码
#include<iostream>
#include<cstring>
using namespace std;
int main()
{
string s;
int a[30];
cin>>s;
for (int i=0;i<26;i++)
a[i]=0;
int l=0;
if (s[0]!='?')
a[s[0]-'A']++;
for (int r=1;r<s.length();r++)
{
while (a[s[r]-'A']==1 && l<s.length())
{
a[s[l]-'A']--;
l++;
}
a[s[r]-'A']++;
if (r-l==25)
{
for (int i=l;i<=r;i++)
{
if (s[i]=='?')
for (int j=0;j<26;j++)
if (a[j]==0)
{
s[i]=j+'A';
a[j]=1;
break;
}
cout<<s[i];
}
cout<<endl;
return 0;
}
}
cout<<"-1"<<endl;
return 0;
}
第三题
题意
112123123412345 …这个序列由连续正整数组成的若干部分构成,其中第一部分包含1至1之间的所有数字,第二部分包含1至2之间的所有数字,第三部分包含1至3之间的所有数字,第i部分总是包含1至i之间的所有数
字,11212312341234512345612345671234567812345678912345678910,其中第1项是1,第3项是2,第20项是5,第38项是2,第56项是0。现在想知道第 k 项数字是多少。
Input
输入由多行组成。
第一行一个整数q表示有q组询问(1<=q<=500)
接下来第i+1行表示第i个输入ki,表示询问第ki项数字
ki<e18
Output
输出包含q行
第i行输出对询问ki的输出结果
思路
从数据范围来看,模拟可以拿60分,可以在没有思路时先把模拟写了拿60分。
对于100分,我们可以看出,这是一个数学题。
我们可以将这个数列给划分一下,分成i层,第i层表示1234…i,然后对于i层,设存在数字j,使得1<=j<=i,这样我们就可以使用二分法。
先二分i,求出ki输出哪一层,然后ki减去之前i-1层的长度,然后二分j,求出ki对应那个数字j,然后减去j-1的长度,然后找出ki对应数字j的哪一个就可以了。
注意
做到第三题了要注意时间,如果时间不够先写一个模拟拿60分真的很香的。
代码
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
long long getsum1(long long x)
{
long long a=1,d=1,n=0,sum=10,Sn=0;
while (sum<=x)
{
n=sum-sum/10; //项数
Sn=Sn+a*n+n*(n-1)*d/2; //前缀和
a=a+n*d+1; //首项
d++; //公差
sum=sum*10; //以10为一组
}
n=x-sum/10+1; //在当前组的位置
return Sn+a*n+n*(n-1)*d/2;
}
long long getsum2(long long x)
{
long long d=1,n=0,sum=10,Sn=0;
while (sum<=x)
{
n=sum-sum/10; //项数
Sn=Sn+n*d; //前缀和
d++; //公差
sum=sum*10; //以10为一组
}
n=x-sum/10+1; //在当前组的位置
return Sn+n*d;
}
int main()
{
int n,tot;
long long x,l,r,mid,x1,x2;
int a[100];
cin>>n;
for (int i=0;i<n;i++)
{
scanf("%lld",&x);
l=0; r=1e9;
while (l<=r)
{
mid=(l+r)/2;
if (getsum1(mid)<x)
{
l=mid+1;
x1=mid;
}
else
r=mid-1;
}
x=x-getsum1(x1);
l=0; r=x1+1;
while (l<=r)
{
mid=(l+r)/2;
if (getsum2(mid)<x)
{
l=mid+1;
x2=mid;
}
else
r=mid-1;
}
x=x-getsum2(x2);
x2++;
tot=0;
while (x2!=0)
{
tot++;
a[tot]=x2%10;
x2=x2/10;
}
cout<<a[tot-x+1]<<endl;
}
return 0;
}