Codeforces Round (2021-07-22)

Published 2021-07-21 16:00 1700 words 9 min read Handwritten Every word here was typed by me.

Zhiheng Wang avatar

Zhiheng Wang

技术文章、个人随笔、项目与作品体验记录

This post is not yet available in English. Showing the original.
Harbour.Space Scholarship Contest 2021-2022 (Div. 1 + Div. 2) A. Digits Sum 题目链接 记 $S(x)$ 为 $x$ 的十进制数位和。给定 $n$,问 $1\le x\le n$ 里有多少个数满足 $S(x+1)<S(x)$。 末位不是 9,加一以后数位和也只会加一。末位是 9 就会进位,至少少掉一个 9,数位和一定变小。...

Harbour.Space Scholarship Contest 2021-2022 (Div. 1 + Div. 2)

A. Digits Sum

题目链接

题意

记 S(x)S(x) 为 xx 的十进制数位和。给定 nn,问 1≤x≤n1\le x\le n 里有多少个数满足 S(x+1)<S(x)S(x+1)<S(x)。

思路

末位不是 9,加一以后数位和也只会加一。末位是 9 就会进位,至少少掉一个 9,数位和一定变小。

所以数一下 9、19、29 这些数就行。先取 n / 10,末位正好是 9 时再加一个。

代码

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

typedef long long ll;
const int maxn = 1e6 + 17;

int T, n, m, ans;
int a[maxn];
string str;

int main() {
	cin >> T;
	while(T--) {
		cin >> n;
		ans = n / 10;
		if(n % 10 == 9) ans++;
		cout << ans << endl;
	}
	return 0;
}

B. Reverse String

题目链接

题意

在字符串 ss 中任选一个位置出发,先向右走若干步,再向左走若干步,两个阶段都可以不走。每到一个位置就记下字符,问能不能得到 tt。

思路

枚举 tt 在哪里掉头,再枚举在 ss 中从哪里出发。掉头前是一段向右的子串;掉头后把目标串反过来,就也能用子串比较了。

注意掉头的那个字符只记一次。右半段从它左边开始取,最左端不能越过 0。这里直接用 substr 比较,数据范围够用。

代码

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

typedef long long ll;
const int maxn = 1e6 + 17;

int T, n, m, ans;
int a[maxn];
string s, t;

int main() {
	cin >> T;
	while(T--) {
		cin >> s >> t;
		int flg = 0;
		int sl = s.length();
		int len = t.length();
		for(int i = 0; i < len; i++) {
			string l = t.substr(0, i + 1);
			string r = t.substr(i + 1, len - i - 1);
			reverse(r.begin(), r.end());
			int lenl = l.length(), lenr = r.length();
			for(int j = 0; j + i < sl; j++) {
				if(l == s.substr(j, i + 1) && j + i - lenr >= 0) {
					if(r == s.substr(j + i - lenr, lenr)) flg = 1;
				}
			}
		}
		cout << (flg ? "YES" : "NO") << endl;
	}
	return 0;
}

C. Penalty

题目链接

题意

两队交替罚点球,各有五次机会。已知十脚球的结果,其中 ? 可以进也可以不进;一方已经不可能追平时就结束,问最早第几脚能结束。

思路

想让比赛早点结束,就尽量拉开比分。分别让第一队的 ? 全进、第二队全不进,再反过来算一次,取较早的结果。

每踢一脚就更新得分和剩余次数。如果领先的分数已经超过对面剩下的机会,就可以停了。

代码

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

typedef long long ll;
const int maxn = 1e6 + 17;

int T, n, m, ans;
int a[maxn], r[2], s[2];
string str;

int main() {
	cin >> T;
	while(T--) {
		ans = 10;
		cin >> str;
		for(int i = 1; i <= 10; i++) {
			if(str[i - 1] == '1') a[i] = 1;
			else if(str[i - 1] == '0') a[i] = 0;
			else a[i] = -1;
		}
		r[1] = 5, r[0] = 5, s[1] = 0, s[0] = 0;
		for(int i = 1; i <= 10; i++) {
			if(a[i] == 1) {
				s[i & 1]++, r[i & 1]--;

			}
			else if(a[i] == 0) {
				r[i & 1]--;
			}
			else{
				if(i & 1) {
					s[i & 1]++, r[i & 1]--;
				}
				else{
					r[i & 1]--;
				}
			}
			// cout << s[1] << r[1] << s[0] << r[0] << endl;
			if(s[1] - s[0] > r[0] || s[0] - s[1] > r[1]) {
				ans = min(ans, i);
				break;
			}
		}
		r[1] = 5, r[0] = 5, s[1] = 0, s[0] = 0;
		for(int i = 1; i <= 10; i++) {
			if(a[i] == 1) {
				s[i & 1]++, r[i & 1]--;

			}
			else if(a[i] == 0) {
				r[i & 1]--;
			}
			else{
				if(i & 1) {
					r[i & 1]--;
				}
				else{
					s[i & 1]++, r[i & 1]--;
				}
			}
			// cout << s[1] << r[1] << s[0] << r[0] << endl;
			if(s[1] - s[0] > r[0] || s[0] - s[1] > r[1]) {
				ans = min(ans, i);
				break;
			}
		}
		cout << ans << endl;
	}
	return 0;
}

D. Backspace

题目链接

题意

按顺序输入字符串 ss,每个字符都可以改成按一次退格。退格会删掉当前最后一个字符,空串时没有效果,问最后能不能得到 tt。

思路

保留下来的字符之间,没用的字符必须两两消掉,所以跳过时一次跳两个。第一个保留字符前面可以全部按退格,奇偶两种起点都试一下。

从左往右匹配,当前字符相同就保留,否则跳两个。最后还要看剩下的后缀是不是偶数个,不能只匹配完 tt 就算成功。读字符前先检查边界。

代码

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

typedef long long ll;

int T, n, m, ans;
string s, t;

int main() {
	cin >> T;
	while(T--) {
		int flg = 0;
		cin >> s >> t;
		int ls = s.length(), lt = t.length();
		int ps = 0, pt = 0;
		while(ps < ls && s[ps] != t[0]) ps += 2;
		for(; ps < ls && pt < lt; ) {
			if(s[ps] == t[pt]) ps++, pt++;
			else ps += 2;
		}
		if(pt == lt && (ls - ps) % 2 == 0) flg = 1;
		ps = 1, pt = 0;
		while(ps < ls && s[ps] != t[0]) ps += 2;
		for(; ps < ls && pt < lt; ) {
			if(s[ps] == t[pt]) ps++, pt++;
			else ps += 2;
		}
		if(pt == lt && (ls - ps) % 2 == 0) flg = 1;
		cout << (flg ? "YES" : "NO") << endl;
	}
	return 0;
}

E. Permutation Shift

题目链接

题意

把 1,2,…,n1,2,\ldots,n 先循环右移 kk 位,再交换至多 mm 对元素,得到了给定排列,其中 m≤n/3m\le n/3。求所有可能的 kk。

思路

一次交换最多动两个位置,所以平移后至少有 n−2mn-2m 个位置原本就是对的。位置 ii 上的数是 aia_i,它对应的平移量就是 (i−ai+n) mod n(i-a_i+n)\bmod n,先按这个统计候选。

再对每个候选真正算交换次数。把当前值和它应该去的位置连起来,用并查集数环;一个长度为 LL 的环需要 L−1L-1 次交换,总共就是 n−环数n-\text{环数}。

所有平移量的计数加起来是 nn,每个候选至少占 n/3n/3,所以最多只剩三个候选,不会每种平移都扫一遍。

代码

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

typedef long long ll;

const int maxn = 3e5 + 17;
int T, n, m, ans;
vector<int> v;
int a[maxn], cnt[maxn], fa[maxn];

int getf(int x) {
	return fa[x] == x ? x : (fa[x] = getf(fa[x]));
}

void merge(int x, int y) {
	int fx = getf(x), fy = getf(y);
	if(fx == fy) return;
	fa[fy] = fx;
	return;
}

int main() {
	cin >> T;
	while(T--) {
		ans = 0;
		v.clear();
		cin >> n >> m;
		for(int i = 0; i <= n; i++) cnt[i] = 0;

		for(int i = 1; i <= n; i++) cin >> a[i];
		for(int i = 1; i <= n; i++) {
			cnt[(i - a[i] + n) % n]++;
		}
		for(int k = 0; k < n; k++) if(cnt[k] >= n - 2 * m) {
			int tmp = 0;
			for(int i = 1; i <= n; i++) fa[i] = i;
			for(int i = 1; i <= n; i++) {
				int to = ((i - k + n) % n);
				if(!to)  to = n;
				// if(to == a[i]) fa[to] = 0;
				merge(a[i], to);
			}
			// for(int i = 1; i <= n; i++) cout << getf(i) << " \n"[i == n];
			for(int i = 1; i <= n; i++) {
				if(getf(i) == i) tmp++;
			}
			if(n - tmp <= m) ans++, v.push_back(k);
		}
		cout << ans;
		int len = v.size();
		cout << (len ? " " : "\n");
		for(int i = 0; i < len; i++) cout << v[i] << " \n"[i == len - 1];
	}
	return 0;
}

Written by Handwritten

Random Posts

© 2026 Zhiheng Wang @traceonsnow
Powered by theme astro-koharu · Inspired by Shoka
Loading...