1. 程式人生 > 其它 >AtCoder Beginner Contest 247題解

AtCoder Beginner Contest 247題解

A - Move Right

題目描述:給你一個長度為\(4\)01串,讓你將它右移一位並將高位補0後輸出。

思路:根據題意模擬即可

時間複雜度:\(O(1)\)

參考程式碼:

void solve() {
	string s;
	cin >> s;
	s = '0' + s.substr(0, 3);
	cout << s << '\n';
	return;
}

B - Unique Nicknames

題目描述:有\(n\)個人,每個人有兩個候選的名字,問你是否存在一種分配方案,使得每個人的名字都不在其他人的候選名字中出現。

思路:考慮到資料量比較小,考慮讀入之後暴力檢驗即可。

時間複雜度:\(O(n^2|S|)\)\(|S|\)為字串的長度

參考程式碼:

void solve() {
	int n;
	cin >> n;
	vector<vector<string>>strs(n, vector<string>(2));
	for (int i = 0; i < n; ++i) cin >> strs[i][0] >> strs[i][1];
	for (int i = 0; i < n; ++i) {
		int cnt = 0;
		for (int k = 0; k <= 1; ++k) {
			for (int j = 0; j < n; ++j) {
				if (i == j) continue;
				if (strs[j][0] != strs[i][k] && strs[j][1] != strs[i][k]) continue;
				++cnt;
				break;
			}
		}
		if (cnt == 2) {
			cout << "No" << '\n';
			return;
		}
	}
	cout << "Yes" << '\n';
	return;
}

C - 1 2 1 3 1 2 1

題目描述:定義序列\(S_i\),其中\(S_1 = (1)\),有遞推式\(S_i = S_{i - 1}\;i\;S_{i - 1}\),現在給定\(n\),輸出\(S_n\)

思路:根據題意模擬即可。

時間複雜度:\(O(2^n)\)

參考程式碼:

void solve() {
	int n;
	cin >> n;
	vector<int> res = { 1 };
	for (int i = 2; i <= n; ++i) {
		int m = res.size();
		vector<int>ans(2 * m + 1, 0);
		for (int k = 0; k < m; ++k) ans[k] = ans[m + k + 1] = res[k];
		ans[m] = i;
		swap(res, ans);
	}
	for (auto&& re : res) cout << re << ' ';
	cout << '\n';
	return;
}

D - Cylinder

題目描述:有一個佇列,有\(Q\)次操作,操作有以下兩種型別:

  • 1 x c:表示向佇列中插入\(c\)個值為\(x\)的元素
  • 2 c:表示從佇列中取出\(c\)個元素並輸出這c個元素的值的和

資料範圍:\(1 \leq Q \leq 2 \times 10^5\)

思路:將每次插入當做一個整體儲存在佇列中,考慮到出隊的數量可能小於當前隊首的數量,所以使用雙端佇列維護即可。

時間複雜度:\(O(n)\)

參考程式碼:

void solve() {
	int q;
	cin >> q;
	using PII = pair<int, int>;
	deque<PII> deq;
	while (q--) {
		int op, x, c;
		cin >> op;
		if (op == 1) {
			cin >> x >> c;
			deq.push_back({ c , x });
		}
		else {
			cin >> c;
			long long res = 0;
			while (true) {
				auto [nums, val] = deq.front(); deq.pop_front();
				if (nums < c) {
					c -= nums;
					res += 1ll * nums * val;
				}
				else {
					nums -= c;
					res += 1ll * c * val;
					deq.push_front({ nums , val });
					break;
				}
			}
			cout << res << '\n';
		}
	}
	return;
}

E - Max Min

題目描述:給你一個長度為\(n\)的陣列\(A\),和兩個整數\(x , y (x \leq y)\),問你有多少個區間\([L , R]\),滿足區間的最大值為\(y\),最小值為\(x\)

思路:首先考慮到值小於\(x\)或者大於\(y\)的位置一定不能對答案做出貢獻,所以我們可以將陣列分解成最少的不重疊子串,使得對於每一段中的元素都在區間\([x , y]\)內。現在討論其中的一個子串,假設這個子串的長度為\(m\),我們使用雙指標,統計出現的數字,若\(x , y\)在區間\([lr , rs]\)內恰好都出現, 那麼此時對答案的貢獻為\(m - rs + 1\),然後我們移動左指標直到某一個臨界值不在區間內,再去移動右指標,重複即可。

時間複雜度:\(O(n)\)

參考程式碼:

void solve() {
	int n, a, x, y;
	cin >> n >> y >> x;
	vector<int> nums;
	long long res = 0;
	auto cal = [&](){
		int m = nums.size();
		if (m == 1) return;
		int rs = 1, cntx = nums[1] == x, cnty = nums[1] == y;
		for (int lr = 1; lr < m; ++lr) {
			while (rs < m && (cntx == 0 || cnty == 0)) {
				if (++rs >= m) break;
				cntx += nums[rs] == x;
				cnty += nums[rs] == y;
			}
			
			res += m - rs;
			cntx -= nums[lr] == x;
			cnty -= nums[lr] == y;
		}
		return;
	};
	nums.push_back(0);
	for (int i = 1; i <= n; ++i) {
		cin >> a;
		if (a >= x && a <= y) nums.push_back(a);
		else {
			cal();
			nums.clear();
			nums.push_back(0);
		}
	}
	cal();
	cout << res << '\n';
	return;
}

F - Cards

題目描述:給你\(n\)張卡片,卡片的正面的數字集合是一個\(n\)的排列,卡片的背面的數字集合也是一個\(n\)的排列,問你有多少種選擇方案可以使得選出來的卡片包含有\(1 \sim n\)的所有數字,答案對998244353取模。

思路:考慮到卡片正反面都是排列,若將數字\(i\)抽象成編號為\(i\)的頂點,對於一張卡片上的數字,假設正面為\(u\),背面為\(v\),那麼我們假定頂點對\((u , v)\)之間存在一條有向邊\(u\to v\),那麼按照這樣就可以構建成一個有向圖,考慮到是排列,那麼這個有向圖就是由一個又一個不相交的環所組成。對於一個環上的數字,任意相鄰兩個就是一張卡片,那麼要表示這個環所對應的數字集合,假設這個環上有\(m\)個頂點,即求:對於任意兩個相鄰的頂點必須選擇其中一個的方案數。假設共有\(k\)個環,第\(i\)個環的頂點數為\(V_i\),設其對答案的貢獻為\(g(V_i)\),根據乘法原理最終答案為:

\[\prod\limits_{i = 1}^{k}g(V_i) \]

時間複雜度:\(O(n)\)

參考程式碼:

const int mod = 998244353;

void solve() {
	int n;
	cin >> n;
	vector<vector<int>>f1(n + 1, vector<int>(2, 0));//選擇第一個
	vector<vector<int>>f2(n + 1, vector<int>(2, 0));//不選第一個
	f1[1][1] = 1;
	f2[1][0] = 1;
	for (int i = 2; i <= n; ++i) {
		f1[i][0] = f1[i - 1][1];
		f1[i][1] = (f1[i - 1][0] + f1[i - 1][1]) % mod;
		f2[i][0] = f2[i - 1][1];
		f2[i][1] = (f2[i - 1][0] + f2[i - 1][1]) % mod;
	}
	auto calCircle = [&](int m)->int {
		if (m == 1) return 1;
		return (1ll * f1[m][1] + f1[m][0] + f2[m][1]) % mod;
	};
	vector<int>adj(n + 1), a(n + 1), b(n + 1);
	for (int i = 1; i <= n; ++i) cin >> a[i];
	for (int i = 1; i <= n; ++i) {
		cin >> b[i];
		adj[a[i]] = b[i];
	}
	vector<bool>vis(n + 1, false);
	auto dfs = [&](auto&& dfs, int rt, int u)->int {
		vis[u] = true;
		if (u == rt) return 1;
		return dfs(dfs, rt, adj[u]) + 1;
	};
	int res = 1;
	for (int i = 1; i <= n; ++i) {
		if (vis[a[i]]) continue;
		int j = dfs(dfs , a[i], b[i]);
		res = 1ll * res * calCircle(j) % mod;
	}
	cout << res << '\n';
	return;
}