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


比賽傳送門

A. Digits Sum

顯然 \(S(x+1)<S(x)\) 當且僅當 \(x\) 個位是 9,所以列個式子即可。

#include <bits/stdc++.h>
#define repeat(i, a, b) for (int i = (a), ib = (b); i < ib; i++)
#define repeat_back(i, a, b) for (int i = (b) - 1, ib = (a);i >= ib; i--)
using namespace std;
namespace start {
	typedef long long ll; const int inf = INT_MAX >> 1; const ll INF = INT64_MAX >> 1;
		ll read() { ll x; if (scanf("%lld", &x) != 1) exit(0); return x; } // will detect EOF
		void print(ll x, bool e = 0) { printf("%lld%c", x, " \n"[e]); }
	const int N = 500010;
} using namespace start;
#define OK { puts("Yes"); return; }
#define GG { puts("No"); return; }
void Solve() {
	int n = read();
	print((n + 1) / 10, 1);
}
signed main() {
	// freopen("data.txt", "r", stdin);
	int T = 1; T = read();
	repeat (ca, 1, T + 1) {
		Solve();
	}
	return 0;
}

B. Reverse String

字符串做太少了,我瞎想了個哈希。

枚舉 s 串的的第一種操作的起點和終點,這對應了 s 串的一個子串,用哈希判斷其是否位 t 串的前綴(第一個操作)。然后 for 循環判斷 t 串的后綴是否合法(第二個操作)。(這個 for 循環也可以哈希 / kmp,為了手速就不寫了)

#include <bits/stdc++.h>
#define repeat(i, a, b) for (int i = (a), ib = (b); i < ib; i++)
#define repeat_back(i, a, b) for (int i = (b) - 1, ib = (a);i >= ib; i--)
using namespace std;
namespace start {
	typedef long long ll; const int inf = INT_MAX >> 1; const ll INF = INT64_MAX >> 1;
		ll read() { ll x; if (scanf("%lld", &x) != 1) exit(0); return x; } // will detect EOF
		void print(ll x, bool e = 0) { printf("%lld%c", x, " \n"[e]); }
	const int N = 500010;
} using namespace start;
#define OK { puts("Yes"); return; }
#define GG { puts("No"); return; }
mt19937 rnd(chrono::high_resolution_clock::now().time_since_epoch().count());
int x = rnd() % 100;
template<typename string>
struct Hash{
	template<int b,int mod>
	struct hs{
		vector<int> a,p;
		hs(const string &s=""){
			a={0},p={1};
			for(auto c:s){
				a.push_back((1ll*a.back()*b+(c^x))%mod);
				p.push_back(1ll*p.back()*b%mod);
			}
		}
		ll q(int l,int r){
			return (a[r+1]-1ll*a[l]*p[r-l+1]%mod+mod)%mod;
		}
	};
	hs<257,1000000007> h1;
	hs<257,2147483647> h2;
	Hash(const string &s):h1(s),h2(s){}
	pair<ll, ll> query(int l,int r){
		return {h1.q(l,r),h2.q(l,r)};
	}
};
char s[N], t[N];
void Solve() {
	scanf("%s%s", s, t);
	int n = strlen(s), m = strlen(t);
	Hash<string> ss(s), tt(t);
	repeat (i, 0, m)
	repeat (j, 0, n - i)
	if (ss.query(j, j + i) == tt.query(0, i)) {
		int f = true;
		repeat (p, i + 1, m)
		if (t[p] != s[i + j - p + i]) {
			f = false;
			break;
		}
		if (f) { OK; }
	}
	GG;
}
signed main() {
	// freopen("data.txt", "r", stdin);
	int T = 1; T = read();
	repeat (ca, 1, T + 1) {
		Solve();
	}
	return 0;
}

C. Penalty

非常容易可得,要么 A 球隊贏要么 B 球隊贏要么平(廢話)。

對於前一種情況,我們要讓所有 ? 盡可能讓 A 得分,至少不讓 B 得分。每個球后判斷 A 是否穩贏了。第二種情況也類似。

#include <bits/stdc++.h>
#define repeat(i, a, b) for (int i = (a), ib = (b); i < ib; i++)
#define repeat_back(i, a, b) for (int i = (b) - 1, ib = (a);i >= ib; i--)
using namespace std;
namespace start {
	typedef long long ll; const int inf = INT_MAX >> 1; const ll INF = INT64_MAX >> 1;
		ll read() { ll x; if (scanf("%lld", &x) != 1) exit(0); return x; } // will detect EOF
		void print(ll x, bool e = 0) { printf("%lld%c", x, " \n"[e]); }
	const int N = 500010;
} using namespace start;
#define OK { puts("Yes"); return; }
#define GG { puts("No"); return; }
char s[N];
void Solve() {
	scanf("%s", s);
	auto cnt = array<int, 2>{0};
	int ans = 10;
	repeat (i, 0, 10) {
		if (s[i] == '1') cnt[i % 2]++;
		else if (s[i] == '?' && i % 2 == 1) cnt[1]++;
		if ((9 - i) / 2 < cnt[1] - cnt[0])
			ans = min(ans, i + 1);
	}
	cnt = array<int, 2>{0};
	repeat (i, 0, 10) {
		if (s[i] == '1') cnt[i % 2]++;
		else if (s[i] == '?' && i % 2 == 0) cnt[0]++;
		if ((9 - i + 1) / 2 < cnt[0] - cnt[1])
			ans = min(ans, i + 1);
	}
	print(ans, 1);
}
signed main() {
	// freopen("data.txt", "r", stdin);
	int T = 1; T = read();
	repeat (ca, 1, T + 1) {
		Solve();
	}
	return 0;
}

D. Backspace

貪心。

顯然一開始可以進行任意次 backspace,就不太好操作。考慮反過來,如果 s 最后一個字符和 t 最后相同,就進行匹配,否則這個字符將替換位 backspace(這導致這個字符和它前一個字符都從 s 中消失)。

#include <bits/stdc++.h>
#define repeat(i, a, b) for (int i = (a), ib = (b); i < ib; i++)
#define repeat_back(i, a, b) for (int i = (b) - 1, ib = (a);i >= ib; i--)
using namespace std;
namespace start {
	typedef long long ll; const int inf = INT_MAX >> 1; const ll INF = INT64_MAX >> 1;
		ll read() { ll x; if (scanf("%lld", &x) != 1) exit(0); return x; } // will detect EOF
		void print(ll x, bool e = 0) { printf("%lld%c", x, " \n"[e]); }
	const int N = 500010;
} using namespace start;
#define OK { puts("Yes"); return; }
#define GG { puts("No"); return; }
#define int ll
char s[N], t[N];
void Solve() {
	scanf("%s%s", s, t);
	int n = strlen(s), m = strlen(t);
	int p = m - 1;
	repeat_back (i, 0, n) {
		if (p >= 0 && s[i] == t[p]) {
			p--;
		} else {
			i--;
		}
	}
	if (p == -1) OK;
	GG;
}
signed main() {
	// freopen("data.txt", "r", stdin);
	int T = 1; T = read();
	repeat (ca, 1, T + 1) {
		Solve();
	}
	return 0;
}

E. Permutation Shift

初看真的被嚇到了,什么 shift,還交換。但是做法真的好簡單。()

這題的關鍵是 m 的范圍 \(0 \le m \le \dfrac{n}{3}\)

首先,先 shift 再交換,還是先交換再 shift,其實沒區別。我們先 shift p 數組,然后交換來讓 p 變回 identity 排列。

排列變回 identity 需要的交換次數是多少?(群論的知識不多講了,我語言能力太菜)次數是 (排列長度 - 排列分解為循環置換的個數)。

由於 m 最大為 \(\dfrac{n}{3}\),直接可以推出 shift 操作后 \(p_i = i\) 的個數至少為 \(\dfrac{n}{3}\)

所以問題就很簡單了,答案的個數最多為 3,我們只要統計 \((p_i - i)\bmod n\) 的個數。如果大於等於 \(\dfrac n 3\),就跑一遍把 p 左移 \((p_i - i)\bmod n\) 后的排列,統計它的循環置換個數。

#include <bits/stdc++.h>
#define repeat(i, a, b) for (int i = (a), ib = (b); i < ib; i++)
#define repeat_back(i, a, b) for (int i = (b) - 1, ib = (a);i >= ib; i--)
using namespace std;
namespace start {
	typedef long long ll; const int inf = INT_MAX >> 1; const ll INF = INT64_MAX >> 1;
		ll read() { ll x; if (scanf("%lld", &x) != 1) exit(0); return x; } // will detect EOF
		void print(ll x, bool e = 0) { printf("%lld%c", x, " \n"[e]); }
	const int N = 500010;
} using namespace start;
#define OK { puts("Yes"); return; }
#define GG { puts("No"); return; }
int Count(int a[],int n){
	static bool vis[N];
	int cnt=0;
	fill(vis,vis+n,0);
	repeat(i,0,n)if(!vis[i]){
		for(int p=a[i];p!=i;p=a[p])
			vis[p]=1;
		cnt++;
	}
	return cnt;
}
vector<int> ans;
int a[N], cnt[N];
int b[N];
void Solve() {
	ans.clear();
	int n = read(), k = read();
	fill(cnt, cnt + n, 0);
	repeat (i, 0, n) {
		a[i] = read() - 1;
		cnt[(i + n - a[i]) % n]++;
	}
	repeat (i, 0, n) {
		if (cnt[i] >= n / 3) {
			repeat (j, 0, n) b[j] = a[(j + i) % n];
			if (n - Count(b, n) <= k)
				ans.push_back(i);
		}
	}
	sort(ans.begin(), ans.end());
	print(ans.size());
	repeat (i, 0, ans.size())
		print(ans[i], i == ib - 1); 
}
signed main() {
	// freopen("data.txt", "r", stdin);
	int T = 1; T = read();
	repeat (ca, 1, T + 1) {
		Solve();
	}
	return 0;
}

F. Pairwise Modulo

這題 \(a_i \neq a_j\) 太有用了,去掉我就不會做了。

把問題分為兩部分:

  1. 維護一個集合,支持插入 x,查詢 \(\displaystyle\sum_{i\in \text Set}i \bmod x\)
  2. 維護一個集合,支持插入 x,查詢 \(\displaystyle\sum_{i\in \text Set}x \bmod i\)

對於第一個部分,先把 \(i \bmod x\) 寫成 \(i-x\lfloor\dfrac{i}{x}\rfloor\)\(\sum i\) 簡單,x 是常量,\(\sum\lfloor\dfrac{i}{x}\rfloor\) 可以用線段樹 / 樹狀數組維護(單點修改,區間求和),在線段樹的集合元素位置記錄 1,查詢 \([x, 2x-1][2x,3x-1]...\)。咋一看好像 \(O(n)\) 次查詢,實則因為 \(a_i\) 互不相等,所以平均 \(O(\log n)\) 查詢,復雜度 \(O(n\log^2n)\)

對於第二部分,還是把 \(x \bmod i\) 寫成 \(x-i\lfloor\dfrac{x}{i}\rfloor\)。如果暴力數論分塊 + 線段樹,復雜度 \(O(n\sqrt n\log n)\) 過不了,所以需要反一下。考慮把 i 加入集合時對未來 x 的貢獻,在 \([i, 2i-1][2i,3i-1]...\) 這些位置區間加 1, 2, ...。所以就是區間加單點查詢的線段樹 / 樹狀數組,\(O(n\log^2n)\)

#include <bits/stdc++.h>
#define repeat(i, a, b) for (int i = (a), ib = (b); i < ib; i++)
#define repeat_back(i, a, b) for (int i = (b) - 1, ib = (a);i >= ib; i--)
using namespace std;
namespace start {
	typedef long long ll; const int inf = INT_MAX >> 1; const ll INF = INT64_MAX >> 1;
		ll read() { ll x; if (scanf("%lld", &x) != 1) exit(0); return x; } // will detect EOF
		void print(ll x, bool e = 0) { printf("%lld%c", x, " \n"[e]); }
	const int N = 500010;
} using namespace start;
#define OK { puts("Yes"); return; }
#define GG { puts("No"); return; }
#define int ll
struct zkw {
	const ll a0 = 0;
	int n; ll a[N * 4];
	void init(int inn, ll in[] = nullptr) { // A[x] = a0 or in[x]
		n = 1; while (n < inn) n <<= 1;
		fill(a + n, a + n * 2, a0);
		if (in) repeat (i, 0, inn) a[n + i] = in[i];
		repeat_back (i, 1, n) up(i);
	}
	void up(int x) { // private
		a[x] = a[x * 2] + a[x * 2 + 1];
	}
	void add(int x, ll k) { // A[x] += k
		x += n;
		a[x] += 1;
		while (x >>= 1) up(x);
	}
	ll sum(int l, int r) { // U(A[l, r])
		ll ans = a0; l += n - 1, r += n + 1;
		while (l + 1 < r){
			if (~l & 1) ans = (ans + a[l + 1]);
			if ( r & 1) ans = (ans + a[r - 1]);
			l >>= 1, r >>= 1;
		}
		return ans;
	}
} zkw, tr;
int ans[N];
void Solve() {
	int n = read();
	zkw.init(300000); tr.init(300000);
	ll sum = 0;
	repeat (i, 0, n) {
		int x = read(); sum += x;
		zkw.add(x, 1);
		if (i) ans[i] = ans[i - 1]; 
		ans[i] += sum;
		ans[i] += x * i - tr.sum(0, x);
		for (int j = x; j <= 300000; j += x) {
			// orz(j, j + x - 1);
			// cout << tr.qb(j, j + x - 1) << endl; pause;
			ans[i] -= zkw.sum(j, min(300000ll, j + x - 1)) * j;
			tr.add(j, j / x * x);
			tr.add(min(300000ll, j + x - 1) + 1, -j / x * x);
		}
		print(ans[i], i == n - 1);
	}
}
signed main() {
	// freopen("data.txt", "r", stdin);
	int T = 1; // T = read();
	repeat (ca, 1, T + 1) {
		Solve();
	}
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM