CSP 模测二

A - HRZ 的序列

相较于咕咕东,瑞神是个起早贪黑的好孩子,今天早上瑞神起得很早,刷B站时看到了一个序列aa,他对这个序列产生了浓厚的兴趣,他好奇是否存在一个数KK,使得一些数加上KK,一些数减去KK,一些数不变,使得整个序列中所有的数相等,其中对于序列中的每个位置上的数字,至多只能执行一次加运算或减运算或是对该位置不进行任何操作。由于瑞神只会刷B站,所以他把这个问题交给了你!

输入

输入第一行是一个正整数tt表示数据组数。接下来对于每组数据,输入的第一个正整数nn表示序列aa的长度,随后一行有nn个整数,表示序列aa。

输出

输出共包含tt行,每组数据输出一行。对于每组数据,如果存在这样的KK,输出"YES",否则输出“NO”。(输出不包含引号)

样例输入

2
5
1 2 3 4 5
5
1 2 3 4 5

样例输出

NO
NO

思路

综述

需要判断有几种不同的数字,如果不同的数字个数超过三个则输出NO;
如果正好是三个则判断是否据中间的数字左右对称,若是则输出YES,否则输出NO;
此外输出YES;

总结

1、思路较简单,数据范围是需要铭记的
2、数据范围longlong问题,大坑

代码

#include <iostream>
#include <algorithm>
using namespace std;
long long t;
long long n;
long long x, y, z;
long long a[10050];
long long tot = 0;
long long flag1, flag2;
int main() {
	cin >> t;
	for (long long i = 0; i < t; i++) {
		cin >> n;
		tot = 0;
		flag1 = 0;
		flag2 = 1;//判断是否有多个
		for (long long j = 0; j < n; j++) {
			cin >> a[j];
			if (flag2 == 0) {
				continue;
			}
			if (flag1 == 0){
				x = a[j];
				flag1++;
				continue;
			}if (flag1 == 1 && a[j] != x) {
				flag1++;
				y = a[j];
				continue;
			}if (flag1 == 2 && a[j] != x && a[j] != y) {
				z = a[j];
				flag1++;
				continue;
			}
			if (flag1 == 3 && a[j] != x && a[j] != y && a[j] != z) {
				flag2 = 0;
			}
		}

		if (flag2 == 0) {
			cout << "NO" << endl;
		}
		else {
			if (flag1 < 3)cout << "YES" << endl;
			else {
				if (x > y)swap(x, y);
				if (x > z)swap(x, z);
				if (y > z)swap(y, z);

				long long num1 = z - y;
				long long num2 = y - x;
				if (num1 == num2)cout << "YES" << endl;
				else cout << "NO" << endl;
			}
		}
	}
}

B - HRZ 学英语

瑞神今年大三了,他在寒假学会了英文的26个字母,所以他很兴奋!于是他让他的朋友TT考考他,TT想到了一个考瑞神的好问题:给定一个字符串,从里面寻找连续的26个大写字母并输出!但是转念一想,这样太便宜瑞神了,所以他加大了难度:现在给定一个字符串,字符串中包括26个大写字母和特殊字符’?’,特殊字符’?'可以代表任何一个大写字母。现在TT问你是否存在一个位置连续的且由26个大写字母组成的子串,在这个子串中每个字母出现且仅出现一次,如果存在,请输出从左侧算起的第一个出现的符合要求的子串,并且要求,如果有多组解同时符合位置最靠左,则输出字典序最小的那个解!如果不存在,输出-1!这下HRZ蒙圈了,他刚学会26个字母,这对他来说太难了,所以他来求助你,请你帮他解决这个问题,报酬是可以帮你打守望先锋。说明:字典序先按照第一个字母,以A、B、C…Z的顺序排列;如果第一个字母一样,那么比较第二个、第三个乃至后面的字母。如果比到最后两个单词不一样长(比如,SIGH和SIGHT),那么把短者排在前。例如
AB??EFGHIJKLMNOPQRSTUVWXYZ
ABCDEFGHIJKLMNOPQRSTUVWXYZ
ABDCEFGHIJKLMNOPQRSTUVWXYZ
上面两种填法,都可以构成26个字母,但是我们要求字典序最小,只能取前者。注意,题目要求的是第一个出现的,字典序最小的!

输入

输入只有一行,一个符合题目描述的字符串。

输出

输出只有一行,如果存在这样的子串,请输出,否则输出-1

样例输入

ABC??FGHIJK???OPQR?TUVWXY?

AABCDEFGHIJKLMNOPQRSTUVW??M

样例输出

ABCDEFGHIJKLMNOPQRSTUVWXYZ

-1

思路

综述 & 过程

尺取法,取26个相邻字母[L,R]看是否符合
1、如果出现两个相同的字母,则不符合,L++,R++;
2、如果符合,输出

输出要求:

从L开始输出,遇到非问号就输出,否则就输出不存在的字母的第一个,这样保证了字典序是最小的

总结

1、遇到大坑,相邻组数据之间需要初始化操作;
2、尺取法

代码

没必要统计问号个数,只需要判断是否存在两个同的字母即可

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
string s;
int num[26];
int main() {
	cin >> s;
	int l = 0;
	int r = 25;
	int flag = true;


	for (int i = 0; i < 26; i++)num[i] = 0;

	while (r < s.size()) {
		for (int i = 0; i < 26; i++)num[i] = 0;
		int wenhao = 0;
		flag = true;

		for (int i = l; i <= r; i++) {
			if (s[i] == '?') {
				wenhao++;
				continue;
			}
			else {
				int x = s[i] - 'A';
				num[x]++;
				if (num[x] > 1) {
					flag = false;
					break;
				}
			}
		}
		if (flag == false) {
			l++;
			r++;
			continue;
		}
		else {
			char ch;
			for (int m = l; m <= r; m++) {
				if (s[m] != '?')cout << s[m];
				else {
					for (int k = 0; k < 26; k++) {
						if (num[k] != 0)continue;
						else {
							ch = 'A' + k;
							cout << ch;
							num[k] = 1;
							break;
						}
					}
				}
			}
			flag = true;
			break;
		}
	}
	if (flag == false)cout << "-1" << endl;
}

C - 咕咕东的奇妙序列

咕咕东正在上可怕的复变函数,但对于稳拿APlusAPlus的咕咕东来说,她早已不再听课,此时她在睡梦中突然想到了一个奇怪的无限序列:112123123412345…112123123412345…这个序列由连续正整数组成的若干部分构成,其中第一部分包含11至11之间的所有数字,第二部分包含11至22之间的所有数字,第三部分包含11至33之间的所有数字,第ii部分总是包含11至ii之间的所有数字。所以,这个序列的前5656项会是1121231234123451234561234567123456781234567891234567891011212312341234512345612345671234567812345678912345678910,其中第11项是11,第33项是22,第2020项是55,第3838项是22,第5656项是00。咕咕东现在想知道第kk项数字是多少!但是她睡醒之后发现老师讲的东西已经听不懂了,因此她把这个任务交给了你。

输入

输入由多行组成。
第一行一个整数qq表示有qq组询问(1<=q<=500)(1<=q<=500)
接下来第i+1i+1行表示第ii个输入kiki​,表示询问第kiki​项数字.(1<=ki<=1018)(1<=ki​<=1018)

输出

输出包含q行
第ii行输出对询问的输出结果。

样例输入

5
1
3
20
38
56

样例输出

1
2
5
2
0

思路

当时拿到题,还有一个小时时间,先是写了个枚举前30分的算法,然后又写了枚举60分。虽然没拿到60分,只拿了30分,但是这种思想还是蛮重要的,即分步得分;
根据等差数列的性质计算。虽然看起来与等差数列不同,但是仍然是等差数列。其中数列的通项为1,2,……x的长度,数列和为长度相加即总长度。
在第1到第9块,每相邻的两块之间数字个数相差1;
第10块到第99块,每相邻的两块之间数字个数相差2;
第10i块到10i+1-1块,每相邻的两块之间数字个数相差i + 1。
找位置,用二分的方法。
一次用来查找它在第几块,一次用来查找它在块中的位置,就能找出它的值。

总结

1、二分法的应用应熟练;
2、分布得分,写枚举算法可以拿到大半的分数,如果时间不够或者思路缺少注意使用;
(类似模板)如下所示:

#include <iostream>
using namespace std;
int main(){
	scanf("%d%d",&n,&m);
	if(n<=10 && m<=10) test1();
	else if (n<=50 && m<=50 ) test2();
	else if (n<=300 && m<=300 ) test3();
	//... 
}
void test1(){
 	
 }
void test2(){
 	
 }
void test3(){
 	
 }

代码

上文有详细注释

#include<iostream>
using namespace std;
long long q,k;
const long long  maxn=1e9;


long long getsum(long long x){
	return x*(x+1)/2;
}

long long sum1(long long x){
	long long ans =0,i=1,j=1;
	for ( ; j*10<=x; i++,j*=10 ) {
		ans += i*getsum(j*9) + i*j*9*(x-j*10+1);
	}
	return ans + i*getsum(x-j+1);	
}

long long sum2 ( long long x ) { // 获取sum[x]

	long long ans=0, i=1, j=1;
	for ( ; j*10<=x; i++,j*=10 ) {
		ans += i*j*9;
	}
	return ans + i*(x-j+1);
}



long long location(long long k){
	long long left,right;
	long long ans1,ans2;
	left=0;
	right=maxn;
	while(left<=right){
		long long mid = (left+right)/2;
		if(sum1(mid)<k){
			left = mid+1;
			ans1 = mid;
		}
		else right = mid-1;
	}
	k -= sum1(ans1);
	left = 0;
	right = ans1+1;
	while(left<=right){
		long long mid = (left+right)/2;
		if(sum2(mid)<k){
			left=mid+1;
			ans2 = mid;
		}else right = mid-1;
	} 
	k -=sum2(ans2++);
	
		int a[30];
		for(int i=0;i<30;i++)a[i]=0;
		
		int i = 0;
        while(ans2) {
            a[++i] = ans2%10;
            ans2 /= 10;
        }
        return a[i-k+1];
        
}


int main(){
	
	cin>>q;
	for(int i=0;i<q;i++){
		
		cin>>k;
		
		cout<<location(k)<<endl;
		
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值