CSP第二次模拟赛

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;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值