【比赛链接】
【题解链接】
**【A】**Regular Triangle
【思路要点】
- 判断是否 A = B = C A=B=C A=B=C 。
- 时间复杂度 O ( 1 ) O(1) O(1) 。
【代码】
#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 x, y, z; read(x), read(y), read(z); if (x == y && x == z) puts("Yes"); else puts("No"); return 0; }
**【B】**Red or Blue
【思路要点】
- 按题意模拟即可。
- 时间复杂度 O ( N ) O(N) O(N) 。
【代码】
#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(""); } char s[MAXN]; int main() { int n, r = 0, b = 0; read(n); scanf("\n%s", s + 1); for (int i = 1; i <= n; i++) if (s[i] == 'R') r++; else b++; if (r > b) puts("Yes"); else puts("No"); return 0; }
**【C】**Snuke the Wizard
【思路要点】
- 可以发现,若某处的傀儡走到了 1 1 1 号点的左侧,那么初始时在其左侧的傀儡都会走到 1 1 1 号点的左侧;若某处的傀儡走到了 N N N 号点的右侧,那么初始时在其右侧的傀儡都会走到 N N N 号点的右侧。
- 因此,我们就可以通过二分找到最右侧的走到 1 1 1 号点的左侧的傀儡和最左侧的走到 N N N 号点的右侧的傀儡。
- 时间复杂度 O ( Q L o g N ) O(QLogN) O(QLogN) 。
【代码】
#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, q; char s[MAXN], c[MAXN], opt[MAXN]; bool gol(int pos) { for (int i = 1; i <= q; i++) { if (c[i] == s[pos]) { if (opt[i] == 'L') pos--; else pos++; } if (pos > n) return false; if (pos < 1) return true; } return false; } bool gor(int pos) { for (int i = 1; i <= q; i++) { if (c[i] == s[pos]) { if (opt[i] == 'L') pos--; else pos++; } if (pos > n) return true; if (pos < 1) return false; } return false; } int getl() { int l = 0, r = n; while (l < r) { int mid = (l + r + 1) / 2; if (gol(mid)) l = mid; else r = mid - 1; } return l; } int getr() { int l = 1, r = n + 1; while (l < r) { int mid = (l + r) / 2; if (gor(mid)) r = mid; else l = mid + 1; } return n + 1 - l; } int main() { read(n), read(q); scanf("\n%s", s + 1); for (int i = 1; i <= q; i++) scanf("\n%c %c", &c[i], &opt[i]); writeln(n - getl() - getr()); return 0; }
**【D】**Modulo Operations
【思路要点】
- 考虑一个给定的取模序列 a 1 , a 2 , a 3 , . . . , a N a_1,a_2,a_3,...,a_N a1,a2,a3,...,aN ,其中可能产生作用的是作为前缀最小值出现的 a i a_i ai ,在其余的 a i a_i ai 处,当前数值已经不足 a i a_i ai ,可以视作没有进行取模。
- 考虑将数组从大到小排序,对于可能产生作用的 a i a_i ai 进行动态规划。
- 记 f i , j f_{i,j} fi,j 表示考虑前 i i i 大的模数,得数为 j j j 的排列方式数,则有转移
f i + 1 , j % a i + 1 ⇐ f i , j f_{i+1,j\%a_{i+1}}\Leftarrow f_{i,j} fi+1,j%ai+1⇐fi,j
f i + 1 , j ⇐ f i , j × ( N − ( i + 1 ) ) f_{i+1,j}\Leftarrow f_{i,j}\times(N-(i+1)) fi+1,j⇐fi,j×(N−(i+1))- 其中转移 1 1 1 代表认为 a i a_i ai 可能产生作用,转移 2 2 2 代表将 a i a_i ai 任意排列在后面的某个位置,不对数值产生影响。
- 时间复杂度 O ( N × X ) O(N\times X) O(N×X) 。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 205; const int MAXM = 1e5 + 5; const int P = 1e9 + 7; 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, x, a[MAXN], dp[MAXN][MAXM]; void update(int &x, int y) { x += y; if (x >= P) x -= P; } int main() { read(n), read(x), dp[0][x] = 1; for (int i = 1; i <= n; i++) read(a[i]); sort(a + 1, a + n + 1, [&] (int x, int y) {return x > y; }); for (int i = 1; i <= n; i++) for (int j = 0; j <= x; j++) { int tmp = dp[i - 1][j]; update(dp[i][j % a[i]], tmp); update(dp[i][j], 1ll * tmp * (n - i) % P); } int ans = 0; for (int i = 0; i <= x; i++) update(ans, 1ll * dp[n][i] * i % P); writeln(ans); return 0; }
**【E】**Black or White
【思路要点】
- 分两种情况计算答案:
- ( 1 ) (1) (1) 、在吃第 i i i 块之前,已经没有白巧克力剩余。
- 此时第 i i i 块一定是黑巧克力,记 n w i nw_{i} nwi 表示吃完第 i i i 块后没有白巧克力剩余的概率,情况 ( 1 ) (1) (1) 对答案的贡献应当为 n w i − 1 nw_{i-1} nwi−1 。
- 计算 n w i nw_{i} nwi 有 n w i = n w i − 1 + ( i − 1 W − 1 ) 2 i nw_{i}=nw_{i-1}+\frac{\binom{i-1}{W-1}}{2^i} nwi=nwi−1+2i(W−1i−1)
- ( 2 ) (2) (2) 、在吃第 i i i 块之前,两种巧克力均有剩余。
- 此时每一种使得两种巧克力均有剩余的情况是等概率发生的,考虑计算 l f t i lft_i lfti 表示吃完第 i i i 块后两种巧克力均有剩余的情况总数,情况 ( 2 ) (2) (2) 对答案的贡献应当为 l f t i − 1 2 i \frac{lft_{i-1}}{2^i} 2ilfti−1 。
- 计算 l f t i lft_{i} lfti 有 l f t i = 2 l f t i − 1 − ( i − 1 B − 1 ) − ( i − 1 M − 1 ) lft_{i}=2lft_{i-1}-\binom{i-1}{B-1}-\binom{i-1}{M-1} lfti=2lfti−1−(B−1i−1)−(M−1i−1)
- 时间复杂度 O ( B + W ) O(B+W) O(B+W) 。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 2e5 + 5; const int P = 1e9 + 7; 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 fac[MAXN], inv[MAXN], two[MAXN], itwo[MAXN]; int power(int x, int y) { if (y == 0) return 1; int tmp = power(x, y / 2); if (y % 2 == 0) return 1ll * tmp * tmp % P; else return 1ll * tmp * tmp % P * x % P; } int getc(int x, int y) { if (y > x) return 0; else return 1ll * fac[x] * inv[y] % P * inv[x - y] % P; } void init(int n) { fac[0] = two[0] = itwo[0] = 1; for (int i = 1; i <= n; i++) { fac[i] = 1ll * fac[i - 1] * i % P; two[i] = 2ll * two[i - 1] % P; itwo[i] = (P + 1ll) / 2 * itwo[i - 1] % P; } inv[n] = power(fac[n], P - 2); for (int i = n - 1; i >= 0; i--) inv[i] = inv[i + 1] * (i + 1ll) % P; } int n, m, nw[MAXN], lft[MAXN]; //nw : P(No White Left) //lft : Cnt(Both Left) void update(int &x, int y) { x += y; if (x >= P) x -= P; } int main() { read(n), read(m); init(n + m), lft[0] = 1; for (int i = 1; i <= n + m; i++) { nw[i] = nw[i - 1], lft[i] = 2ll * lft[i - 1] % P; update(nw[i], 1ll * itwo[i] * getc(i - 1, m - 1) % P); if (i >= n) update(lft[i], P - getc(i - 1, n - 1)); if (i >= m) update(lft[i], P - getc(i - 1, m - 1)); writeln((nw[i - 1] + 1ll * itwo[i] * lft[i - 1]) % P); } return 0; }
**【F】**More Realistic Manhattan Distance
【思路要点】
- 通过合适的预处理,我们可以 O ( 1 ) O(1) O(1) 回答距离某一行或列向某一方向上最近的某一朝向的路所在的位置。
- 将起点和终点上方和下方第一个 E E E 方向的行和第一个 W W W 方向的行,以及起点和终点左方和右方第一个 S S S 方向的列和第一个 N N N 方向的列选出,在形成的交叉点上直接运行最短路算法即可。
- 我们至多会选出 6 6 6 行 6 6 6 列,因此时间复杂度为 O ( N + K L o g K × Q ) O(N+KLogK\times Q) O(N+KLogK×Q) ,其中 K = 36 K=36 K=36 。
【代码】
#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(""); } namespace ShortestPath { const ll INF = 1e18; const int MAXP = 1e6; struct edge {int dest, len; }; int n; ll dist[MAXP]; vector <edge> a[MAXP]; set <pair <ll, int> > st; void addedge(int x, int y, int z) { a[x].push_back((edge) {y, z}); } void init(int x) { n = x; st.clear(); for (int i = 1; i <= n; i++) { dist[i] = INF; a[i].clear(); } } void work(int s) { dist[s] = 0; st.insert(make_pair(0, s)); while (!st.empty()) { pair <ll, int> tmp = *st.begin(); st.erase(tmp); for (unsigned i = 0; i < a[tmp.second].size(); i++) { int dest = a[tmp.second][i].dest; ll newlen = tmp.first + a[tmp.second][i].len; if (newlen < dist[dest]) { st.erase(make_pair(dist[dest], dest)); dist[dest] = newlen; st.insert(make_pair(dist[dest], dest)); } } } } } int n, m, q; char h[MAXN], v[MAXN]; int hpre[MAXN][2], hsuf[MAXN][2]; int vpre[MAXN][2], vsuf[MAXN][2]; int nx, ny; vector <int> x, y; bool sx[MAXN], sy[MAXN]; void addx(int pos) { if (!sx[pos]) { sx[pos] = true; x.push_back(pos); } } void addy(int pos) { if (!sy[pos]) { sy[pos] = true; y.push_back(pos); } } int path(pair <int, int> s, pair <int, int> t) { nx = x.size(), ny = y.size(); static int point[16][16]; int tot = 0; for (int i = 0; i < nx; i++) for (int j = 0; j < ny; j++) point[i][j] = ++tot; ShortestPath :: init(tot); for (int i = 0; i < nx; i++) for (int j = 0; j < ny; j++) { if (h[x[i]] == 'E' && j < ny - 1) ShortestPath :: addedge(point[i][j], point[i][j + 1], y[j + 1] - y[j]); if (h[x[i]] == 'W' && j > 0) ShortestPath :: addedge(point[i][j], point[i][j - 1], y[j] - y[j - 1]); if (v[y[j]] == 'S' && i < nx - 1) ShortestPath :: addedge(point[i][j], point[i + 1][j], x[i + 1] - x[i]); if (v[y[j]] == 'N' && i > 0) ShortestPath :: addedge(point[i][j], point[i - 1][j], x[i] - x[i - 1]); } for (int i = 0; i < nx; i++) for (int j = 0; j < ny; j++) if (x[i] == s.first && y[j] == s.second) ShortestPath :: work(point[i][j]); for (int i = 0; i < nx; i++) for (int j = 0; j < ny; j++) if (x[i] == t.first && y[j] == t.second) { ll tmp = ShortestPath :: dist[point[i][j]]; if (tmp > 1e8) return -1; else return tmp; } assert(false); return -1; } int main() { read(n), read(m), read(q); scanf("\n%s\n%s", h + 1, v + 1); for (int i = 1; i <= n; i++) { hpre[i][0] = hpre[i - 1][0]; hpre[i][1] = hpre[i - 1][1]; if (h[i] == 'E') hpre[i][1] = i; else hpre[i][0] = i; } for (int i = n; i >= 1; i--) { hsuf[i][0] = hsuf[i + 1][0]; hsuf[i][1] = hsuf[i + 1][1]; if (h[i] == 'E') hsuf[i][1] = i; else hsuf[i][0] = i; } for (int i = 1; i <= m; i++) { vpre[i][0] = vpre[i - 1][0]; vpre[i][1] = vpre[i - 1][1]; if (v[i] == 'S') vpre[i][1] = i; else vpre[i][0] = i; } for (int i = m; i >= 1; i--) { vsuf[i][0] = vsuf[i + 1][0]; vsuf[i][1] = vsuf[i + 1][1]; if (v[i] == 'S') vsuf[i][1] = i; else vsuf[i][0] = i; } sx[0] = sy[0] = true; for (int i = 1; i <= q; i++) { pair <int, int> s, t; read(s.first), read(s.second); read(t.first), read(t.second); addy(vsuf[s.second][0]); addy(vsuf[s.second][1]); addy(vpre[s.second][0]); addy(vpre[s.second][1]); addy(vpre[t.second][0]); addy(vpre[t.second][1]); addy(vsuf[t.second][0]); addy(vsuf[t.second][1]); addx(hsuf[s.first][0]); addx(hsuf[s.first][1]); addx(hpre[s.first][0]); addx(hpre[s.first][1]); addx(hpre[t.first][0]); addx(hpre[t.first][1]); addx(hsuf[t.first][0]); addx(hsuf[t.first][1]); sort(x.begin(), x.end()); sort(y.begin(), y.end()); writeln(path(s, t)); for (auto pos : x) sx[pos] = false; for (auto pos : y) sy[pos] = false; x.clear(), y.clear(); } return 0; }