A. Simple Design
题意:找大于等于\(x\)的第一个数位和是\(k\)的倍数的数。
\(k\)很小,则答案不会大于\(x\)很多。暴力枚举即可。
点击查看代码
void solve() {int n, k;std::cin >> n >> k;for (int i = n; ; ++ i) {int x = i, s = 0;while (x) {s += x % 10;x /= 10;}if (s % k == 0) {std::cout << i << "\n";return;}}
}
B. Haunted House
题意:给你一个\(01\)串,每次你可以移动相邻的两个数,求对于每个\(i\),让\(1 - i\)位都是\(0\)最小操作数。
我们从低到高模拟,我们每次都要移动一段\(1\),那么我们记录\(1\)的数量,当遇到一个零的时候就代表前面这些1都需要移动一次使得\(cnt_1\)位是0。
点击查看代码
void solve() {int n;std::cin >> n;std::string s;std::cin >> s;std::vector<i64> ans(n, -1);i64 sum = 0, cnt = 0;for (int i = n - 1; i >= 0; -- i) {if (s[i] == '1') {++ cnt;} else {sum += cnt;ans[i + cnt] = sum;}}std::reverse(ans.begin(), ans.end());for (int i = 0; i < n; ++ i) {std::cout << ans[i] << " \n"[i == n - 1];}
}
C. Medium Design
题意:选一些线段,每个线段使得一个区间的数加一。使得最大值减最小值最大。
一个错误的贪心是先求一个最大值的位置,然后不包含这个位置的线段都不选,然后求出来最小值。
这个的\(hack\)为\(m = 3, (1, 2), (1, 2), (2, 3), (2, 3), (3, 3)\)。发现不选所有的\((1, 2)\)答案是\(3\),但按照这个贪心不能求出正确答案。
正确思路是记录每个点包含它的线段,以及包含它的线段完整包含了几次整个区间。可以先按线段左端点排序,然后枚举点一个一个线段加,在用个优先队列存右端点,每次删去不合法的对头。
注意需要离散化。
点击查看代码
void solve() {int n, m;std::cin >> n >> m;std::vector<std::pair<int, int>> a(n);std::vector<int> b;for (int i = 0; i < n; ++ i) { int l, r;std::cin >> l >> r;a[i] = {l, r};b.push_back(l);b.push_back(r);}b.push_back(1);b.push_back(m);std::sort(b.begin(), b.end());b.erase(std::unique(b.begin(), b.end()), b.end());auto get = [&](int x) -> int {return std::lower_bound(b.begin(), b.end(), x) - b.begin() + 1;};int k = b.size();std::vector<int> d(k + 2);for (auto & [l, r] : a) {l = get(l), r = get(r);}std::sort(a.begin(), a.end());using PII = std::pair<int, int>;std::priority_queue<PII, std::vector<PII>, std::greater<PII>> heap;int pre = 0, suf = 0;int ans = 0;for (int i = 1, j = 0; i <= k; ++ i) {while (heap.size() && heap.top().first < i) {if (heap.top().second == 1) {-- pre;}heap.pop();}while (j < n && a[j].first == i) {if (a[j].first == 1) {++ pre;}if (a[j].second == k) {++ suf;}heap.push({a[j].second, a[j].first});++ j;}ans = std::max(ans, (int)heap.size() - std::min(pre, suf));}std::cout << ans << "\n";
}
D. Counting Rhyme
题意:求\(n\)个数里有多少对数没有共同的因子出现在这些数里面。
\(a_k | a_i, a_k | a_j\)则\(a_k | (a_i, a_j)\)。
那么我们枚举最大公约数,记\(g_i\)为有多少对数的最大公约数是\(i\)的数量,\(cnt_i\)为\(i\)的倍数的数量。
那么有\(g_i = \frac{cnt_i(cnt_i - 1)}{2} - \sum_{i | j} g_j\)。对于一个数\(x\),如果它出现过则所有最大公约数是\(x\)倍数的对都不能选。
点击查看代码
void solve() {int n;std::cin >> n;std::vector<i64> cnt(n + 1), st(n + 1);for (int i = 0; i < n; ++ i) {int x;std::cin >> x;++ cnt[x];st[x] = 1;}std::vector<i64> g(n + 1);for (int i = 1; i <= n; ++ i) {for (int j = i + i; j <= n; j += i) {cnt[i] += cnt[j];}}for (int i = n; i >= 1; -- i) {g[i] = cnt[i] * (cnt[i] - 1) / 2;for (int j = i + i; j <= n; j += i) {g[i] -= g[j];}}for (int i = 1; i <= n; ++ i) {if (st[i]) {for (int j = i; j <= n; j += i) {g[j] = 0;}}}i64 ans = std::accumulate(g.begin(), g.end(), 0ll);std::cout << ans << "\n";
}