【CodeForces】CodeForces Round #517 (Div. 1 + Div. 2) 題解
阿新 • • 發佈:2018-11-19
【比賽連結】
【題解連結】
**【Div.2 A】**Golden Plate
【思路要點】
- 直接迴圈計算答案即可。
- 時間複雜度 。
【程式碼】
#include<bits/stdc++.h> using namespace std; const int MAXN = 2e5 + 5; typedef long long ll; typedef long double ld; typedef unsigned long long ull; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int main() { int n, m, k; read(n), read(m), read(k); int ans = 0; for (int i = 1; i <= k; i++) { int x = n - 4 * (i - 1); int y = m - 4 * (i - 1); ans += x * 2 + y * 2 - 4; } writeln(ans); return 0; }
**【Div.2 B】**Curiosity Has No Limits
【思路要點】
- 首先,每一位的方案是相互獨立的,我們可以分開考慮。
- 當確定 時,合法的 至多隻有 個。
- 因此列舉開頭位置,構造出序列,再檢驗合法性即可。
- 時間複雜度 。
【程式碼】
#include<bits/stdc++.h> using namespace std; const int MAXN = 2e5 + 5; typedef long long ll; typedef long double ld; typedef unsigned long long ull; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int n, a[MAXN], b[MAXN], x[MAXN]; int func(int a, int b, int x) { if (a == b) return a; else if (a == 0) return 0; else return 1 - x; } bool tryans(int f) { x[1] = f; for (int i = 1; i <= n - 1; i++) x[i + 1] = func(a[i] / 2, b[i] / 2, x[i] / 2) * 2 + func(a[i] % 2, b[i] % 2, x[i] % 2); for (int i = 1; i <= n - 1; i++) { if ((x[i] | x[i + 1]) != a[i]) return false; if ((x[i] & x[i + 1]) != b[i]) return false; } return true; } void answer() { printf("YES\n"); for (int i = 1; i <= n; i++) printf("%d ", x[i]); exit(0); } int main() { read(n); for (int i = 1; i <= n - 1; i++) read(a[i]); for (int i = 1; i <= n - 1; i++) read(b[i]); if (tryans(0)) answer(); if (tryans(1)) answer(); if (tryans(2)) answer(); if (tryans(3)) answer(); printf("NO\n"); return 0; }
**【Div.2 C/Div.1 A】**Cram Time
【思路要點】
- 記 表示最大的使得 的整數,答案顯然有上界為 。
- 若我們保證第一天用完剛好 小時或完成所有 個任務,那麼答案將一定會取到上界。只需要從大到小考慮每個任務,優先將其安排在第一天即可。
- 時間複雜度 。
【程式碼】
#include<bits/stdc++.h> using namespace std; const int MAXN = 2e5 + 5; typedef long long ll; typedef long double ld; typedef unsigned long long ull; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } vector <int> a, b; int main() { int n, m; read(n), read(m); int sum = n + m, tot = 0; while (sum >= tot + 1) { tot++; sum -= tot; } for (int i = tot; i >= 1; i--) if (n >= i) { n -= i; a.push_back(i); } else b.push_back(i); writeln(a.size()); for (auto x : a) printf("%d ", x); printf("\n"); writeln(b.size()); for (auto x : b) printf("%d ", x); printf("\n"); return 0; }
**【Div.2 D/Div.1 B】**Minimum path
【思路要點】
- 分層 ,令點 為第 層的點。
- 首先確定答案的第 位 ,再計算得出能夠取到最優答案的第 層的結尾位置以及這些位置最多能夠剩餘的修改次數,層層遞進即可。
- 時間複雜度 。
【程式碼】
#include<bits/stdc++.h> using namespace std; const int MAXN = 2e3 + 5; typedef long long ll; typedef long double ld; typedef unsigned long long ull; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int n, k, dp[MAXN][MAXN]; char s[MAXN][MAXN], ans[MAXN * 2]; int main() { read(n), read(k); for (int i = 1; i <= n; i++) scanf("\n%s", s[i] + 1); memset(dp, -1, sizeof(dp)); if (s[1][1] == 'a' || k >= 1) { ans[1] = 'a'; dp[1][1] = k - (s[1][1] != 'a'); } else { ans[1] = s[1][1]; dp[1][1] = 0; } for (int i = 2, now = 0, dest = 1; i <= 2 * n - 1; i++, swap(now, dest)) { char opt = 'z'; for (int x = 1; x <= n; x++) { int y = i - x; if (y < 0 || y > n || dp[x][y] == -1) continue; if (dp[x][y]) opt = 'a'; if (x + 1 <= n) chkmin(opt, s[x + 1][y]); if (y + 1 <= n) chkmin(opt, s[x][y + 1]); } ans[i] = opt; for (int x = 1; x <= n; x++) { int y = i - x; if (y < 0 || y > n || dp[x][y] == -1) continue; if (dp[x][y]) { if (x + 1 <= n) chkmax(dp[x + 1][y], dp[x][y] - (s[x + 1][y] != 'a')); if (y + 1 <= n) chkmax(dp[x][y + 1], dp[x][y] - (s[x][y + 1] != 'a')); } else { if (x + 1 <= n && s[x + 1][y] == opt) chkmax(dp[x + 1][y], dp[x][y]); if (y + 1 <= n && s[x][y + 1] == opt) chkmax(dp[x][y + 1], dp[x][y]); } } } printf("%s\n", ans + 1); return 0; }
**【Div.2 E/Div.1 C】**Triple Flips
【思路要點】
- 寫一個暴力,事實表明當 ,問題將始終有解,並且 的所有情況均存在一個 步內的解。
- 接下來我們的思路是通過大約 次操作將序列包含 的長度減小至 ,並通過暴力解決剩餘的問題。
- 由於能夠進行的運算元只有大約 次,我們需要保證在 步內將序列包含 的長度減小 ,或是在 步內將序列包含 的長度減小 。
- 以下是筆者的做法:
、判斷當前區間開頭 個元素是否均為 ,若是,用一次操作處理之,並刪去區間開頭 個元素;區間結尾 個元素同理。
、現在我們可以認為區間開頭、結尾 個元素均至多有 個 。若區間開頭 個元素只有 個 ,用一次操作處理之,並刪去區間開頭 個元素,區間結尾 個元素同理。
、現在我們可以認為區間開頭、結尾 個元素均恰好有 個 。若區間開頭 個元素形如 ,用一次操作處理之,並刪去區間開頭 個元素,區間結尾 個元素同理。
、剩餘的唯一情況是區間開頭 個元素形如 ,區間結尾 個元素形如 。可用兩次操作處理之,並刪去區間開頭、結尾的 個元素。- 如此迭代刪除,直至區間長度降至恰好 。
- 時間複雜度 ,使用操作次數不超過 。
【程式碼】
#include<bits/stdc++.h> using namespace std; const int MAXN = 1e5 + 5; typedef long long ll; typedef long double ld; typedef unsigned long long ull; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } vector <int> x, y, z; namespace force { const int MAXN = 13; unordered_map <bitset <MAXN>, bitset <MAXN> > from; bitset <MAXN> q[1 << MAXN], dest; void work(int *a, int n, int delta) { dest.reset(); for (int i = 1; i <= n; i++) dest[i] = a[i]; if (dest == 0) return; int l = 0, r = 0; q[0] = 0, from[0] = 0; while (l <= r) { bitset <MAXN> now = q[l++]; for (int i = 1; i <= n; i++) for (int j = i + 2; j <= n; j += 2) { int k = (i + j) / 2; bitset <MAXN> tmp = now; tmp.flip(i), tmp.flip(j), tmp.flip(k); if (from.count(tmp) == 0) { from[tmp] = now; q[++r] = tmp; if (tmp == dest) { bitset <MAXN> pos = tmp; while (pos != 0) { bitset <MAXN> tnp = pos ^ from[pos]; int val = 0; x.push_back((val = tnp._Find_first()) + delta); y.push_back((val = tnp._Find_next(val)) + delta); z.push_back((val = tnp._Find_next(val)) + delta); pos = from[pos]; } return; } } } } printf("NO\n"); exit(0); } } int n, a[MAXN]; int main() { read(n); for (int i = 1; i <= n; i++) read(a[i]); if (n <= 12) force :: work(a, n, 0); else { int head = 1, back = n; while (back - head >= 12 && a[head] == 0) head++; while (back - head >= 12 && a[back] == 0) back--; while (back - head >= 12) { if (a[head] + a[head + 1] + a[head + 2] == 3) { a[head] = a[head + 1] = a[head + 2] = 0; x.push_back(head); y.push_back(head + 1); z.push_back(head + 2); } else if (a[back] + a[back - 1] + a[back - 2] == 3) { a[back] = a[back - 1] = a[back - 2] = 0; x.push_back(back); y.push_back(back - 1); z.push_back(back - 2); } else if ((head + back) % 2 == 0) { a[head] = a[back] = 0; int mid = (head + back) / 2; a[mid] = 1 - a[mid]; x.push_back(head); y.push_back(back); z.push_back(mid); } else if (a[head + 1] == 1) { a[head + 1] = a[back] = 0; int mid = (head + 1 + back) / 2; a[mid] = 1 - a[mid]; x.push_back(head + 1); y.push_back(back); z.push_back(mid); } else if (a[back - 1] == 1) { a[head] = a[back - 1] = 0; int mid = (head + back - 1) / 2; a[mid] = 1 - a[mid]; x.push_back(head); y.push_back(back - 1); z.push_back(mid); } else if (a[head + 2] == 1) { a[head + 2] = a[head] = 0; a[head + 4] = 1 - a[head + 4]; x.push_back(head); y.push_back(head + 2); z.push_back(head + 4); } else if (a[back - 2] == 1) { a[back - 2] = a[back] = 0; a[back - 4] = 1 - a[back - 4]; x.push_back(back); y.push_back(back - 2); z.push_back(back - 4); } else { int tmp = back - 3; a[head] = 1 - a[head]; a[tmp] = 1 - a[tmp]; int mid = (head + tmp) / 2; a[mid] = 1 - a[mid]; x.push_back(head); y.push_back(tmp); z.push_back(mid); } while (back - head >= 12 && a[head] == 0) head++; while (back - head >= 12 && a[back] == 0) back--; } force :: work(a + head - 1, back - head + 1, head - 1); } printf("YES\n"); assert((int) x.size() <= n / 3 + 12); writeln(x.size()); for (unsigned i = 0; i < x.size(); i++) printf("%d %d %d\n", x[i], y[i], z[i]); return 0; }
**【Div.2 F/Div.1 D】**Familiar Operations
【思路要點】
- 顯然的一點是在本題中,一個數 可以被等價地表示為一個可重集