A. Adjacent Digit Sums
题意:求有没有一个数字各位和为\(x\),并且加一后各位和为\(y\)。
发现一个数加一后,要么和加一,要么进若干位,就是减若干个9然后加1。所以判断\(y == x + 1 || (x - y + 1) \% 9 == 0\)
点击查看代码
void solve() {int x, y;std::cin >> x >> y;if (y == x + 1 || (y < x) && (x - y + 1) % 9 == 0) {std::cout << "YES\n";} else {std::cout << "NO\n";}
}
B. Two Large Bags
题意:有两个袋子,开始第一个袋子有\(n\)个数,你每次可以给一个数给另一个袋子,或者选一个数使得两个袋子都至少有一个这个数然后让第一个袋子里的这个数加一。最后要让两个袋子的数相等。
发现值域和\(n\)同级别,于是可以开个数组记录每个数的出现次数。然后从小到大枚举,尽可能让数字加一。因为至少要一个袋子都有一个,所以每个数要留两个,其余的都加一,如果只有1个这个数,那么说明不可能有数变成它了,就是no。模拟即可。
点击查看代码
void solve() {int n;std::cin >> n;std::vector<int> a(n);for (int i = 0; i < n; ++ i) {std::cin >> a[i];}std::vector<int> cnt(2 * n);for (int i = 0; i < n; ++ i) {++ cnt[a[i]];}for (int i = 1; i < 2 * n; ++ i) {if (cnt[i] == 1) {std::cout << "NO\n";return;} else if (cnt[i] > 2) {cnt[i + 1] += cnt[i] - 2;}}std::cout << "YES\n";
}
C. Devyatkino
题意:给你一个\(n\),你每次可以给它加上一个各位都是9的数。求让\(n\)至少有一位是7的最少操作个数。
一个位置加上9其值一定会改变,发现\(6\)要加9次9能变成7,这是最多的情况,但考虑可能有进位的情况,可能需要加更多次。于是枚举加什么数,然后一直加每次判断\(n\)有没有出现7,记录最小次数就行。至于加多少次,应该不到10次,不过为了保险可以加20次。
点击查看代码
void solve() {i64 n;std::cin >> n;auto check = [&](i64 x) -> bool {while (x) {if (x % 10 == 7) {return true;}x /= 10;}return false;};int ans = 100;for (i64 m = 9; m <= 1e17; m = m * 10 + 9) {i64 x = n;int sum = 0;int t = 20;while (t -- && !check(x)) {x += m;++ sum;}if (check(x)) {ans = std::min(ans, sum);}}std::cout << ans << "\n";
}
D. Object Identification
题意:交互题。给你值域为\([1, n]\)的数组\(x\),还隐藏了一个值域为\([1, n]\)的数组\(y\),没有一对\(x, y\)是相同的。有两种情况,一种是\(x_i\)向\(y_i\)连有向边,一种是\(i\)代表坐标\((x_i, y_i)\)。你不知道是哪种情况,你可以询问最多两次。每次问一个\(i, j\),如果是第一种情况会返回\(i\)到\(j\)的最短路,如果是第二种情况返回\((x_i, y_i), (x_j, y_j)\)的曼哈顿距离。
先看有没有1到n的数没有出现,如果有则他没有出边,随便问一个点,如果是第一种情况答案肯定是0,否则是第二种情况。
如果都出现过,就记\(i, j\)记录\(1\)和\(n\)出现的位置,然后询问\(1, n\)和\(n, 1\),因为只有\(n\)条边,所以如果是情况一两个回答必有一个小于\(n-1\),否则是情况二。
点击查看代码
int ask(int i, int j) {std::cout << "? " << i << " " << j << std::endl;int res;std::cin >> res;return res;
}void solve() {int n;std::cin >> n;std::vector<int> a(n);std::vector<int> st(n + 1);for (int i = 0; i < n; ++ i) {std::cin >> a[i];}int p1 = 0, pn = 0;for (int i = 0; i < n; ++ i) {st[a[i]] = 1;if (a[i] == 1) {p1 = i + 1;}if (a[i] == n) {pn = i + 1;}}for (int i = 1; i <= n; ++ i) {if (!st[i]) {int j = 1;while (j == i) {++ j;}if (ask(i, j) == 0) {std::cout << "! A" << std::endl;} else {std::cout << "! B" << std::endl;}return;}}if (ask(p1, pn) < n - 1 || ask(pn, p1) < n - 1) {std::cout << "! A" << std::endl;} else {std::cout << "! B" << std::endl;}
}
E. White Magic
题意:给你一个数组,你要选一个子序列\(a\)使得任意前缀的最小值大于等于其后缀的\(mex\)。问最多选几个数。
首先,只可能选一个0,不然如果一个0在前缀,一个0在后缀,必然不满足条件。
然后,如果不选0则可以选所有非0的数,因为任意后缀的\(mex=0\)。那么我们就需要就选了所有非0数后能不能再选一个0。发现如果0在后面,对所有后缀的\(mex\)的影响更大,所以应该尽可能让0在前面,于是求出所有后缀的\(mex\),然后从前往后枚举,直到第一个0,使得这个0的前面的每个前缀最小值都大于等于后缀\(mex\)。
点击查看代码
void solve() {int n;std::cin >> n;std::vector<int> a(n + 1);for (int i = 1; i <= n; ++ i) {std::cin >> a[i];}std::vector<int> suf(n + 2);std::set<int> s;int mex = 0;for (int i = n; i ; -- i) {s.insert(a[i]);while (s.count(mex)) {++ mex;}suf[i] = mex;}std::vector<int> pre(n + 1);pre[0] = 2e9;for (int i = 1; i <= n; ++ i) {pre[i] = pre[i - 1];if (a[i] != 0) {pre[i] = std::min(pre[i], a[i]);}}int ans = 0;bool flag = true;for (int i = 1; i <= n && a[i] != 0; ++ i) {if (pre[i] < suf[i + 1]) {flag = false;break;}}if (flag && mex > 0) {++ ans;}for (int i = 1; i <= n; ++ i) {ans += a[i] != 0;}std::cout << ans << "\n";
}