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");
	}
}
相關文章
相關標籤/搜索