Codeforces Round 1061 (Div. 2)
A. Pizza Time
题意:有\(n\)个物品,每次分成三部分,你拿走最少的一部分,第二大的丢弃,最大的保留下一轮。求你最多可以拿到多少。
我们可以分\(1, 1, n - 2\)。这样我们得到\(\lfloor \frac{n-3}{2} \rfloor\)个,然后剩下\(3\)个可以拿一个。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
	int n;
	std::cin >> n;
	std::cout << (n - 3) / 2 + 1 << "\n";
}
int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
	std::cin >> t;
	while (t -- ) {
		solve();
	}
	return 0;
}
B. Strange MachineB. Strange Machine
题意:有\(n\)个指令,如果指令是\(A\)就把当前数减一,如果是\(B\)就把当前数除二向下取整,这些指令循环执行。\(q\)次查询求把一个数变成\(0\)的操作数。
如果有除二的指令,则最多执行\(log\)轮,直接模拟即可。
如果没有,则只有减一的指令,答案就是这个数本身。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
	int n, q;
	std::cin >> n >> q;
	std::string s;
	std::cin >> s;
	int cntb = std::ranges::count(s, 'B');
	while (q -- ) {
		int a;
		std::cin >> a;
		if (cntb == 0) {
			std::cout << a << "\n";
		} else {
			int ans = 0;
			for (int i = 0; a ; i = (i + 1) % n) {
				if (s[i] == 'A') {
					-- a;
				} else {
					a /= 2;
				}
				++ ans;
			}
			std::cout << ans << "\n";
		}
	}
}
int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
	std::cin >> t;
	while (t -- ) {
		solve();
	}
	return 0;
}
C. Maximum GCD on Whiteboard
题意:有\(n\)个数,你可以删除最多\(k\)个。对于剩下的数你可以进行以下操作:把这个数分成三个数,这三个的和等于这个数,然后把最大最小的两个数加入,删除当前数。求可以达到达到全局\(gcd\)最大是多少。
观察一下什么数可以分出来两个都有因子\(d\)的数,这个数要么是\(d\)的倍数,可以不操作;否则设这个数是\(x\),则可以分成这三个数:\(d, d + x\% d, x - d - x \% d\)。那么要求这个数大于等于\(4d\)。那么可以计算\([d, 4d]\)中有多少数不是\(d\)的倍数,这些数也要删去。
则可以枚举答案,看需要删去的数的个数是不是小于等于\(k\)。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
	int n, k;
	std::cin >> n >> k;
	std::vector<int> sum(2 * n + 1);
	for (int i = 0; i < n; ++ i) {
		int x;
		std::cin >> x;
		++ sum[x];
	}
	for (int i = 1; i <= 2 * n; ++ i) {
		sum[i] += sum[i - 1];
	}
	for (int i = n; i >= 1; -- i) {
		int cnt = sum[i - 1];
		if (i * 2 <= 2 * n) {
			cnt += sum[i * 2 - 1] - sum[i];
		} 
		if (i * 3 <= 2 * n) {
			cnt += sum[i * 3 - 1] - sum[i * 2];
		}
		if (i * 4 <= 2 * n) {
			cnt += sum[i * 4 - 1] - sum[i * 3];
		}
		if (cnt <= k) {
			std::cout << i << "\n";
			return;
		}
	}
}
int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
	std::cin >> t;
	while (t -- ) {
		solve();
	}
	return 0;
}
D. Find the Last NumberD. Find the Last Number
题意:交互题。有一个隐藏排列,你每次可以让\(p_i(i<n)\)和一个数进行按位与操作,如果结果不为\(0\)则返回\(1\),则返回\(0\)。最多\(2n\)次询问求出\(p_n\)。
可以按位问,计算出这一位在剩下的数里有多少数是\(1\)多少是\(0\),然后把通过查询所有剩下的数可以知道\(p_n\)这一位是什么,那么其它数可以丢弃,剩下的数进行下一位的操作。这样每一位操作后剩下的数都会减半,最终询问数不超过\(2n\)。这里口胡一下证明,大概就是一开始\([1, n]\)里第\(0\)位是\(1\)的个数和是\(0\)的个数相差不超过\(1\),然后我们选择其中一个集合后,因为里面第\(0\)位都相同那么可以都除一个\(2\)再看第\(0\)位,这样相当于把\([1, n]\)放缩到了\([1, \frac{n}{2}]\),然后继续看第\(0\)位,那么显然差距也不超过\(1\)。那么这样从小到大按位做大概每次剩下的数会减半。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
int ask(int i, int x) {
	std::cout << "? " << i << " " << x << std::endl;
	int res;
	std::cin >> res;
	return res;
}
void solve() {
	int n;
	std::cin >> n;
	int m = 0;
	for (int i = 20; i >= 0; -- i) {
		if (n >> i & 1) {
			m = i;
			break;
		}
	}
	int f[20][2]{};
	for (int i = 1; i <= n; ++ i) {
		for (int j = 0; j <= m; ++ j) {
			f[j][i >> j & 1] += 1;
		}
	}
	std::vector<int> a;
	for (int i = 1; i < n; ++ i) {
		a.push_back(i);
	}
	auto b = a;
	b.push_back(n);
	int tot = 0;
	std::vector<int> st(n + 1);
	for (int i = 0; i <= m && b.size() > 1; ++ i) {
		std::vector<int> na[2];
		for (auto & x : a) {
			na[ask(x, 1 << i)].push_back(x);
		}
		if (na[0].size() < f[i][0]) {
			a = na[0];
			std::vector<int> nb;
			for (auto & x : b) {
				if (x >> i & 1) {
					st[x] = 1;
					for (int j = i + 1; j <= m; ++ j) {
						-- f[j][x >> j & 1];
					}
				} else {
					nb.push_back(x);
				}
			}
			b = nb;
		} else {
			a = na[1];
			std::vector<int> nb;
			for (auto & x : b) {
				if (x >> i & 1) {
					nb.push_back(x);
				} else {
					st[x] = 1;
					for (int j = i + 1; j <= m; ++ j) {
						-- f[j][x >> j & 1];
					}
				}
			}
			b = nb;
		}
	}
	for (int i = 1; i <= n; ++ i) {
		if (!st[i]) {
			std::cout << "! " << i << std::endl;
			return;
		}
	}
}
int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
	std::cin >> t;
	while (t -- ) {
		solve();
	}
	return 0;
}
E. Best Time to Buy and Sell Stock
题意:一个数组,两个人博弈。第一个人每次拿走一个数,另一个每次选择一个数使得第一个人后面无法拿走这个数。最后数组的价值为\(\max a_j - a_i(j > i)\)。第一个人希望价值低,第二个人希望价值高,求最后价值。
考虑二分答案。
那么对于每个\(i\),可以对后面每个\(a_j \geq a_i + x\)的\(j\)连边。那么如果第二个人可以选择到一条边的两个点,就\(ok\)。
考虑怎样的图符合这个条件,考虑一个连通块的点数,如果点数为\(2\),对于第二个人,如果操作其中一个点,第一个人会立即操作另一个点,所以点数为\(2\)的连通块其实没有用。如果连通块点数大于等于\(3\),那么如果第一个人如果不管怎么操作都会留下一个点数大于等于\(3\)的连通块,那么第二个人可以选择一个度数大于等于\(2\)的点就赢了。
对于点数小于\(6\)的连通块,我们暴力判断,对于大于等于\(6\)的连通块,第一个人肯定选择其中一个度数最大的点删,那么就可以判断其它点剩下的度数有没有大于等于\(2\)的。因为第一个人删去一个点只会使得其相邻点度数减一,那么如果不选择度数最大的点,剩下最大度数最多减一,不是最优解。那么如果有最大度数的只有一个点,肯定就选择这个点;如果有多个点,记最大度数为\(d\),那么希望能把其它度数也为\(d\)的度数都减一,那么\(d \geq 3\)的情况就无解了,因为其它度数为\(d\)的点减一后度数也大于等于\(2\)。那么对于最大度数都为\(2\)的情况,也就是一条链,显然当点数大于等于\(6\)时无解。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
	int n;
	std::cin >> n;
	std::vector<int> a(n);
	for (int i = 0; i < n; ++ i) {
		std::cin >> a[i];
	}
	auto check = [&](int x) -> bool {
		std::vector<std::vector<int>> adj(n);
		std::set<std::pair<int, int>> s;
		for (int i = n - 1; i >= 0; -- i) {
			auto it = s.end();
			for (int j = 0; j < 3 && it != s.begin(); ++ j) {
				-- it;
				if (a[i] + x <= it->first) {
					adj[i].push_back(it->second);
					adj[it->second].push_back(i);
				} else {
					break;
				}
			}
			s.emplace(a[i], i);
			while (s.size() > 3) {
				s.erase(s.begin());
			}
		}
		int tot = 0;
		std::vector<int> vis(n);
		for (int i = 0; i < n; ++ i) {
			if (vis[i]) {
				continue;
			}
			std::vector<int> a;
			int U = -1;
			auto dfs = [&](auto && self, int u) -> void {
				vis[u] = 1;
				a.push_back(u);
				if (U == -1 || adj[u].size() > adj[U].size()) {
					U = u;
				}
				for (auto & v : adj[u]) {
					if (!vis[v]) {
						self(self, v);
					}
				}
			};
			dfs(dfs, i);
			if (a.size() > 2) {
				if ( ++ tot >= 2) {
					return true;
				}
				if (a.size() >= 6) {
					for (auto & u : a) {
						if (u == U) {
							continue;
						}
						int t = 0;
						for (auto & v : adj[u]) {
							if (v == U) {
								t = 1;
								break;
							}
						}
						if ((int)adj[u].size() - t >= 2) {
							return true;
						}
					}
				} else {
					auto b = a;
					bool ok = true;
					for (auto & u : b) {
						for (auto & v : b) {
							vis[v] = 0;
						}
						vis[u] = 1;
						bool flag = false;
						for (auto & v : b) {
							if (vis[v]) {
								continue;
							}
							a.clear();
							dfs(dfs, v);
							flag |= a.size() >= 3;
						}
						ok &= flag;
					}
					if (ok) {
						return true;
					}
				}
			}
		}
		return false;
	};
	int lo = -1e9, hi = 1e9;
	while (lo < hi) {
		int mid = lo + hi + 1 >> 1;
		if (check(mid)) {
			lo = mid;
		} else {
			hi = mid - 1;
		}
	}
	std::cout << lo << "\n";
}
int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
	std::cin >> t;
	while (t -- ) {
		solve();
	}
	return 0;
}
F1. Strange Operation (Easy Version)
题意:一个排列,你每次可以选择三个位置\(i, j, k\),满足\(i < j < k\),且\(p_i, p_j, p_j\)是连续的三个数,\(p_i > p_j, p_i > p_k\)。然后使得\(p_i - 2, p_j + 1, p_k + 1\)。求可以得到的最小字典序。
从小到大操作,如此反复直到没有可以操作的位置。
对于\(i\)来说,我们希望他和\(i+1, i+2\)操作,这样\(i\)就会换到更前面,这样字典序就会更小。从小到大操作,操作\(i\)后,\(i+2\)可能继续操作换到\(i, i+1\)前面,这样\(i\)又能往前面走。
点击查看代码
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
	int n;
	std::cin >> n;
	std::vector<int> a(n + 1), p(n + 1);
	for (int i = 1; i <= n; ++ i) {
		std::cin >> a[i];
		p[a[i]] = i;
	}
	while (true) {
		bool flag = false;
		for (int i = 1; i <= n - 2; ++ i) {
			if (p[i + 2] < p[i + 1] && p[i + 2] < p[i]) {
				int x = p[i], y = p[i + 1], z = p[i + 2];
				a[x] = i + 1;
				a[y] = i + 2;
				a[z] = i;
				p[i] = z;
				p[i + 1] = x;
				p[i + 2] = y;
				flag = true;
			}
		}
		if (!flag) {
			break;
		}
	}
	for (int i = 1; i <= n; ++ i) {
		std::cout << a[i] << " \n"[i == n];
	}
}
int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
	std::cin >> t;
	while (t -- ) {
		solve();
	}
	return 0;
}
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号