Codeforces Round #607 (Div. 1) Solution
从这里开始
我又不太会 div 1 A? 我菜爆了。。。
Problem A Cut and Paste
暴力模拟一下。
Code
#include <bits/stdc++.h> using namespace std; typedef bool boolean; const int N = 1e6 + 5; const int Mod = 1e9 + 7; int T; int x; int len; char s[N]; int paste(int s1, int t1, int s2) { int i = s2; for (i = s2; i <= x && i - s2 + s1 <= t1; i++) s[i] = s[i - s2 + s1]; return i; } void solve() { scanf("%d", &x); scanf("%s", s + 1); len = strlen(s + 1); int rlen = len; for (int i = 1; i <= x; i++) { int t = s[i] - '0' - 1; len = ((i + (len - i) * 1ll * (t + 1)) % Mod + Mod) % Mod; int qaq = rlen; while (t--) rlen = paste(i + 1, qaq, rlen + 1) - 1; } printf("%d\n", len); } int main() { scanf("%d", &T); while (T--) { solve(); } return 0; }
Problem B Beingawesomeism
不难注意到答案不会超过 4.
- 答案为 0,这个很 trivial
- 答案小于等于 1 显然是存在一个边界被完全覆盖
- 答案小于等于 2,存在一个 A 在角落或者完整的一行或一列为 A
- 答案小于等于 3,存在 1 个 A 在边界上
- 剩下的情况答案为 4
Code
#include <bits/stdc++.h> using namespace std; typedef bool boolean; const int N = 66; int T, n, m; char s[N][N]; int count(int x1, int y1, int x2, int y2) { int x = 0; for (int i = x1; i <= x2; i++) { for (int j = y1; j <= y2; j++) { x += s[i][j] == 'A'; } } return x; } void solve() { scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) { scanf("%s", s[i] + 1); } int c = count(1, 1, n, m); if (!c) { puts("MORTAL"); return; } if ((c == n * m)) { puts("0"); return; } boolean havec = (s[1][1] == 'A' || s[n][1] == 'A' || s[1][m] == 'A' || s[n][m] == 'A'); int L = count(1, 1, n, 1); int R = count(1, m, n, m); if (L == n || R == n) { puts("1"); return; } int U = count(1, 1, 1, m); int D = count(n, 1, n, m); if (U == m || D == m) { puts("1"); return; } if (havec) { puts("2"); return; } for (int i = 1; i <= n; i++) { if (count(i, 1, i, m) == m) { puts("2"); return; } } for (int i = 1; i <= m; i++) { if (count(1, i, n, i) == n) { puts("2"); return; } } if (L + R + U + D) { puts("3"); return; } puts("4"); } int main() { scanf("%d", &T); while (T--) { solve(); } return 0; }
Problem C Jeremy Bearimy
最大的话,显然以重心为根,答案为每个点的深度和
最小的话,显然每个点的子树内至多有 1 个未匹配点,dp 即可。
Code
#include <bits/stdc++.h> using namespace std; typedef bool boolean; const int N = 6e5 + 5; #define ll long long #define pii pair<int, int> const ll llf = (signed ll) (~0ull >> 3); template <typename T> boolean vmin(T& a, T b) { if (a > b) { a = b; return true; } return false; } int T; int n, n2; int sz[N]; vector<pii> G[N]; int get_sz(int p, int fa) { sz[p] = 1; for (auto E : G[p]) { int e = E.first; if (e == fa) continue; sz[p] += get_sz(e, p); } return sz[p]; } int get_g(int p, int fa) { for (auto E : G[p]) { int e = E.first; if (e == fa) continue; if (sz[e] > n) { return get_g(e, p); } } return p; } ll dep[N]; void dfs(int p, int fa) { for (auto E : G[p]) { int e = E.first; int w = E.second; if (e == fa) continue; dep[e] = dep[p] + w; dfs(e, p); } } ll f[N][2]; void dp(int p, int fa) { static ll g[2]; f[p][0] = llf, f[p][1] = 0; for (auto E : G[p]) { int e = E.first; int w = E.second; if (e == fa) continue; dp(e, p); g[0] = g[1] = llf; vmin(g[0], f[p][0] + f[e][0]); vmin(g[0], f[p][1] + f[e][1] + w); vmin(g[1], f[p][0] + f[e][1] + w); vmin(g[1], f[p][1] + f[e][0]); f[p][0] = g[0]; f[p][1] = g[1]; } } void solve() { scanf("%d", &n); n2 = n << 1; for (int i = 1; i <= (n << 1); i++) { G[i].clear(); } for (int i = 1, u, v, t; i < n2; i++) { scanf("%d%d%d", &u, &v, &t); G[u].emplace_back(v, t); G[v].emplace_back(u, t); } get_sz(1, 0); int g = get_g(1, 0); dep[g] = 0; dfs(g, 0); ll ansmx = 0; for (int i = 1; i <= n2; i++) { ansmx += dep[i]; } dp(g, 0); printf("%lld %lld\n", f[g][0], ansmx); } int main() { scanf("%d", &T); while (T--) { solve(); } return 0; }
Problem D Miss Punyverse
考虑如果子树内留下的决策不是已有的合法划分最多的一个,至多使答案增加 1,但这里会减少 1,因此子树内决策只用保留合法划分最多,且未结束划分的一块的和最大。
然后 dp 即可。
Code
#include <bits/stdc++.h> using namespace std; typedef bool boolean; #define ll long long #define pii pair<int, ll> pii operator + (pii a, pii b) { return pii(a.first + b.first, a.second + b.second); } const int N = 3005; int T, n, m; int sz[N]; pii f[N][N]; int a[N], b[N]; vector<int> G[N]; pii work(pii x) { x.first += x.second > 0; x.second = 0; return x; } void dfs(int p, int fa) { static pii g[N]; for (int i = 0; i <= m; i++) f[p][i] = pii(-N, 0); f[p][0] = pii(0, 0); sz[p] = 0; for (auto e : G[p]) { if (e == fa) continue; dfs(e, p); for (int i = 0; i <= sz[p] + sz[e] && i <= m; i++) g[i] = pii(-N, 0); for (int i = 0; i <= sz[p]; i++) { for (int j = 0; j < sz[e] && i + j <= m; j++) { g[i + j] = max(g[i + j], f[p][i] + f[e][j]); if (j < m) g[i + j + 1] = max(g[i + j + 1], f[p][i] + work(f[e][j])); } } for (int i = 0; i <= sz[p] + sz[e] && i <= m; i++) f[p][i] = g[i]; sz[p] += sz[e]; } ++sz[p]; for (int i = 0; i < sz[p]; i++) { f[p][i].second += a[p] - b[p]; } } void solve() { scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) { scanf("%d", b + i); } for (int i = 1; i <= n; i++) { scanf("%d", a + i); } for (int i = 1; i <= n; i++) G[i].clear(); for (int i = 1, u, v; i < n; i++) { scanf("%d%d", &u, &v); G[u].push_back(v); G[v].push_back(u); } dfs(1, 0); int ans = work(f[1][m - 1]).first; printf("%d\n", ans); } int main() { scanf("%d", &T); while (T--) { solve(); } return 0; }
Problem E Kirchhoff's Current Loss
这个 E 感觉挺简单的,可惜我没时间做了,sad.....
考虑 $n$ 个电阻串联,答案为 $r$,这个很显然
考虑 $n$ 个电阻并联,答案为 $n^2r$,证明考虑用均值不等式归纳。
简单归纳一下可以发现,最终一定是等效于 $k$ 个电阻并联。
然后 dp 即可。
#include <bits/stdc++.h> using namespace std; typedef bool boolean; const int N = 2e5 + 5; int T; int n, m, r; char s[1000000]; boolean ispar[N]; vector<int> G[N]; int newnode() { ++n; G[n].clear(); ispar[n] = false; return n; } int id[N]; int build(char* &s) { int p = newnode(); while (true) { while (*s == ' ') s++; if (!*s || *s == ')') return p; if (*s == 'P') { ispar[p] = true; } else if (*s == 'S') { ispar[p] = false; } else if (*s == '(') { G[p].push_back(build(++s)); } else if (*s == '*') { G[p].push_back(newnode()); id[n] = ++m; } s++; } return p; } int f[N], g[N], R[N]; void dfs(int p) { if (G[p].empty()) { f[p] = 1; return; } if (!ispar[p]) { f[p] = 1e9; g[p] = -1; for (auto e : G[p]) { dfs(e); if (f[e] < f[p]) { f[p] = f[e]; g[p] = e; } } } else { f[p] = 0; for (auto e : G[p]) { dfs(e); f[p] += f[e]; } } } void dfs1(int p) { if (G[p].empty()) { R[id[p]] = 1; return; } if (ispar[p]) { for (auto e : G[p]) dfs1(e); } else { dfs1(g[p]); } } int main() { scanf("%d", &T); while (T--) { n = m = 0; scanf("%d", &r); gets(s); char* tmp = s; build(tmp); dfs(1); long long v = 1ll * r * f[1]; for (int i = 1; i <= m; i++) { R[i] = 0; } dfs1(1); printf("REVOLTING"); for (int i = 1; i <= m; i++) printf(" %lld", R[i] * v); putchar('\n'); } return 0; }
Problem F Intergalactic Sliding Puzzle
考虑按顺时针的构成的环排列,不考虑空位置。
注意到大环不会影响环排列,而空位置经过中间的隔板会做一个长度为 $2k + 1$ 的循环移位。
不难发现必要条件是排列的逆序对个数位偶数个,因为这些循环移位均不会改变逆序对奇偶性。
通过打表(或者阅读题解)可以构造把连续 3 个数循环位移的方案。
然后简单构造一下就行了。
Code
#include <bits/stdc++.h> using namespace std; typedef bool boolean; const int N = 128; string operator * (string a, int t) { string b = ""; for (int i = 0; i < t; i++) b += a; return b; } ostream& operator << (ostream& os, vector<int> p) { for (auto x : p) os << x << " "; os << '\n'; return os; } string sl("l"), sr("r"), su("u"), sd("d"); int T, k, L, n; int x, y; vector<int> grid[2]; vector<int> perm; void solve() { cin >> k; L = 2 * k + 1; n = 4 * k + 1; grid[0].resize(L); grid[1].resize(L); string _; for (int i = 0; i < L; i++) { cin >> _; if (_[0] == 'E') { grid[0][i] = -1; x = 0, y = i; } else { grid[0][i] = stoi(_); } } for (int i = 0; i < L; i++) { cin >> _; if (_[0] == 'E') { grid[1][i] = -1; x = 1, y = i; } else { grid[1][i] = stoi(_); } } string ans = ""; while (x != 0 || y != k) { if (x == 0) { if (y < k) { ans += 'r'; swap(grid[x][y], grid[x][y + 1]); y++; } else { ans += 'l'; swap(grid[x][y], grid[x][y - 1]); y--; } } else { if (!y) { ans += 'u'; swap(grid[x][y], grid[x - 1][y]); x--; } else { ans += 'l'; swap(grid[x][y], grid[x][y - 1]); y--; } } } perm.clear(); reverse(grid[1].begin(), grid[1].end()); for (auto x : grid[0]) if (x > 0) perm.push_back(x); for (auto x : grid[1]) if (x > 0) perm.push_back(x); for (auto &x : perm) if (x > L) x = n - (x - L) + 1; int inv = 0; for (int i = 0; i < n; i++) { for (int j = i + 1; j < n; j++) { if (perm[i] > perm[j]) { inv ^= 1; } } } if (inv) { cout << ("SURGERY FAILED") << '\n'; return; } auto rotateL = [&] () { rotate(perm.begin(), perm.begin() + 1, perm.end()); ans += "A"; }; auto rotateR = [&] () { rotate(perm.begin(), perm.end() - 1, perm.end()); ans += "B"; }; auto shift3 = [&] () { swap(perm[0], perm[2]); swap(perm[1], perm[2]); ans += "S"; }; string A = sr * k + sd + sl * (L - 1) + su + sr * k; string B = sl * k + sd + sr * (L - 1) + su + sl * k; string C = sr * k + sd + sl * k + su; string D = sd + sr * k + su + sl * k; string S = string("B") * (k - 1) + "CBCADD" + string("A") * (k - 1); while (perm[0] != 1) rotateR(); for (int i = 2; i <= n - 2; i++) { int d = -1; while (perm[0] != i) d++, rotateL(); if (d & 1) { if (perm[1] == 1) { rotateR(), rotateR(); shift3(), shift3(); d--; rotateL(); } else if (perm[2] == 1) { rotateR(); shift3(); d++; rotateL(); rotateL(); } else { shift3(); rotateL(); d++; } } while (d) { rotateR(), rotateR(); shift3(); d -= 2; } } while (perm[0] != 1) rotateR(); ans += sr * k + sd; cout << "SURGERY COMPLETE\n"; cout << ans << '\n'; cout << "A " << A << '\n'; cout << "B " << B << '\n'; cout << "C " << C << '\n'; cout << "D " << D << '\n'; cout << "S " << S << '\n'; cout << "DONE\n"; } int main() { // freopen("a.txt", "r", stdin); ios::sync_with_stdio(false); cin.tie(0), cout.tie(0); cin >> T; while (T--) { solve(); } return 0; }