Harbour.Space Scholarship Contest 2021-2022 (open for everyone, rated, Div. 1 + Div. 2)

比赛地址c++

A(水题)

题目连接
数组

题目:
定义一个数\(x\)若为 interesting\(x\)各数位加起来的和大于\(x+1\)各数位加起来的和,现给出\(n\),问\(1\sim n\)之间有多少个 interesting 的数spa

解析:
只有个位数为9的时候才会知足题意,那也就是求得有多少个以9结尾的数字便可rest

#include<bits/stdc++.h>
using namespace std;

int main() {
	int T, n;
	scanf("%d",&T);
	while (T--) {
		scanf("%d", &n);
		printf("%d\n", n / 10 + (n % 10 == 9));
	}
}

B(暴力+KMP)

题目连接
⭐⭐code

题目:
给出一个字符串\(s\),能够从任意下标开始,先向右移动(能够不动)再向左移动(能够不动),问是否存在移动过程当中通过的字符轨迹为字符串\(t\)递归

解析:字符串

  1. 因为先向右移动再向左移动,能够笃定两者中必定有一个回文子串,且必定与首或尾相连
  2. 那么就能够暴力枚举回文子串的对称点,判断是否知足 1 中所叙述的条件,并判断较长部分的串是否存在于\(s\)串中便可

C(水题)

题目连接
get

题目:
给出一个点球大战的结果,0 表明未进, 1 表明进球, 表明未知,问判断一方得到胜利至少要踢多少次点球it

解析:
以判断A方胜利为例,则理想状况是当前第\(x\)轮踢球后,小于\(x\)的A方未知(?)的进球全为命中,而B方全不命中的状况下,即便A方后续一个球也踢不进,B方所有命中,也没法追平比分,那也就是暴力判断一下\(1\sim 10\)的轮数是否能够结束比赛class

#include<bits/stdc++.h>
using namespace std;

char s[15];

int main() {
	int T;
	scanf("%d", &T);
	while (T--) {
		scanf("%s", s + 1);
		int t[2] = { 0 };
		int c[2] = { 0 };
		int i;
		for (i = 1; s[i]; ++i) {
			if (s[i] == '?') ++t[i & 1];
			else if (s[i] == '1') ++c[i & 1];
			if (c[0] + t[0] > (10 - i) / 2 + c[1] || c[1] + t[1] > (11 - i) / 2 + c[0]) break;
		}
		printf("%d\n", min(i, 10));
	}
}

D(思惟)

题目连接
⭐⭐

题目:
如今给出一个字符串\(s\),问是否能够将串中键入某字符的操做更改成使用 Backspace(退格键),使得字符串变为\(t\)

解析:

  1. \(t\)只有在为\(s\)的子序列的前提下,才有可能知足题目条件。考虑到一个退格键表明消除两个字符,因此这个子序列相邻的字符在字符串\(s\)中必须距离为奇数。若是从前向后考虑,\(s[?]=t[0]\)中这个\(?\)便没法肯定(与\(t\)[1]字符知足距离的条件才可),那这样想下去对于后续下标来讲就是一个递归的问题了,不太适用于\(O(n)\)的问题
  2. 考虑从后向前,对于最后一个字符,若是更改成退格键,则对应最后一个有效字符的下标就\(-2\),而\(s\)中最后一个没有被替换的字符也是\(t\)中最后一个字符,那也就能够看出,这样能够惟一肯定\(t\)中最后一个字符的下标在原字符串\(s\)中的奇偶性,继续递推,剩余的字符均可以被惟一肯定
#include<bits/stdc++.h>
using namespace std;

const int maxn = 1e5 + 5;
char s[maxn], t[maxn];

int main() {
	int T;
	scanf("%d", &T);
	while (T--) {
		scanf("%s%s", s, t);
		int i = strlen(s) - 1, j = strlen(t) - 1;
		while (j >= 0 && i >= 0) {
			if (s[i] == t[j]) --j, --i;
			else i -= 2;
		}
		printf("%s\n", j >= 0 ? "NO" : "YES");
	}
}

E(思惟+排列)

题目连接
⭐⭐⭐

题目:
\([1,2,3,\dots]\)的序列,能够对他进行两种操做

  • 将序列循环右移\(k\)
  • 进行不超过\(m\)次的数值交换

如今给出\(n,m,\)结果序列,问有多少个知足条件的\(k\)能够经过数值交换获得结果序列,并增序输出\(k\)

解析:

引理: 对于一个长度位\(n\)排列\(a\)和排列\(b\),让\(a=b\)的最小交换次数是\(n\)与将\(a[i]\)\(b[i]\)链接后,数组长度与对应的图中环数的差
证实: 对于无向图中任意一个环,假如环中点的数量为\(c\),则当\(c-1\)个数位置都正确之后,剩下的数位置也正确,并且环内每次交换操做(除了最后一次)至多可使得1个数回归原位,故每一个环均可以少进行1次交换

考虑到\(m\)次数值交换,最多使得\(2m\)个数与原数列位置不一样(每次选择未选过的两个数),那考虑记录结果数列\(p\)中每一个数,对 "相对于\([1,2,3,\dots]\)的偏移量" 所做出的贡献,那若是这个数\(cnt+2*m\ge n\),那就是有可能在交换次数小于等于\(m\)时实现的,这样的粗略断定的基础上再用引理进行\(O(n)\)的准确破案顶。另外经过\(m\)的范围能够计算出,能经过粗略断定的数不超过3个

#include<bits/stdc++.h>
using namespace std;

const int maxn = 3e5 + 5;
int p[maxn];
int cnt[maxn];
bool vis[maxn];
vector<int> v;
int n, m;

bool check(int k) {
	v.clear();
	for (int i = k; i < n; ++i)
		v.push_back(p[i]);
	for (int i = 0; i < k; ++i)
		v.push_back(p[i]);
	int t = n;
	memset(vis, 0, sizeof(vis));
	for (int i = 0; i < n; ++i) {
		if (vis[i]) continue;
		int j = i;
		while (!vis[j])
			vis[j] = true, j = v[j];
		--t;
	}
	return t <= m;
}

int main() {
	int T;
	scanf("%d", &T);
	while (T--) {
		scanf("%d%d", &n, &m);
		for (int i = 0; i < n; ++i) {
			scanf("%d", p + i);
			--p[i];
			++cnt[(i - p[i] + n) % n];
		}
		vector<int> ans;
		for (int i = 0; i < n; ++i)
			if (cnt[i] + 2 * m >= n && check(i))
				ans.push_back(i);
		printf("%d ", ans.size());
		for (auto& i : ans)
			printf("%d ", i);
		printf("\n");
	}
}
相关文章
相关标签/搜索