250706 学习笔记
前言
努力,是梦想与现实之间的桥梁。
惟有意力轶众,所当希求,能于情意一端,处现实之世,而有勇猛奋斗之才,虽屡踣屡僵,终得现其理想。——鲁迅
模拟赛
今天照常,八点整开始打模拟赛,十一点整结束。打了 T1 的 50 分暴力,T2 的 70 分暴力,T3 的 110 分暴力,没有注意到 T4 的 110 分部分分可以骗粉,最后以 280 分收尾。总结一下,没有挂分,算是……不好也不坏?
| # | 总 | A | B | C | D | E |
|---|---|---|---|---|---|---|
| std | 450 | 50 | 70 | 110 | 110 | 110 |
| 3 | 280 | 50 | 70 | 110 | 20 | 30 |
| - | 03:00:00 | 00:10:55\(^*\) | 00:35:44 | 02:37:56 | 06:57:46 | 99:99:99 |
*:一血
A. Sodoku
按题意直接计算即可。
#include <bits/stdc++.h>
using namespace std;
int a[20][20];
int g[20][2] = {2, 2, 2, 5, 2, 8, 5, 2, 5, 5, 5, 8, 8, 2, 8, 5, 8, 8};
int dir[9][2] = {-1, -1, -1, 0, -1, 1, 0, -1, 0, 0, 0, 1, 1, -1, 1, 0, 1, 1};
int main() {
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
for (int i = 1; i <= 13; i++) {
string s;
cin >> s;
s = "#" + s;
if (i == 1 || i == 5 || i == 9 || i == 13) continue;
int x;
if (i == 2) x = 1;
if (i == 3) x = 2;
if (i == 4) x = 3;
if (i == 6) x = 4;
if (i == 7) x = 5;
if (i == 8) x = 6;
if (i == 10) x = 7;
if (i == 11) x = 8;
if (i == 12) x = 9;
for (int j = 1; j <= 13; j++) {
if (j == 1 || j == 5 || j == 9 || j == 13) continue;
int y;
if (j == 2) y = 1;
if (j == 3) y = 2;
if (j == 4) y = 3;
if (j == 6) y = 4;
if (j == 7) y = 5;
if (j == 8) y = 6;
if (j == 10) y = 7;
if (j == 11) y = 8;
if (j == 12) y = 9;
if (s[j] == '.') a[x][y] = 0;
else a[x][y] = s[j] - '0';
}
}
for (int i = 1; i <= 9; i++) {
bitset<10> bs, bs1;
for (int j = 1; j <= 9; j++) {
if (!a[i][j]) continue;
if (bs[a[i][j]]) {
cout << "GRESKA\n";
return 0;
}
bs[a[i][j]] = 1;
}
for (int j = 1; j <= 9; j++) {
if (!a[j][i]) continue;
if (bs1[a[j][i]]) {
cout << "GRESKA\n";
return 0;
}
bs1[a[j][i]] = 1;
}
}
for (int k = 0; k < 9; k++) {
int x = g[k][0], y = g[k][1];
bitset<10> bs;
for (int i = 0; i < 9; i++) {
int tx = x + dir[i][0], ty = y + dir[i][1];
if (!a[tx][ty]) continue;
if (bs[a[tx][ty]]) {
cout << "GRESKA\n";
return 0;
}
bs[a[tx][ty]] = 1;
}
}
cout << "OK\n";
return 0;
}
B. Labirint
由于 \(n,m,k\le100\),本题可以考虑使用搜索的方法解决。因为颜色只有 \(4\) 中,在状态表示中把对应颜色是否被经过用一个二进制数进行状态压缩表示。使用 \(v_{i,j,k}\) 表示从起点到达 \((i,j)\) 且通过的颜色状态为 \(k\),然后直接进行 BFS。
这样做,颜色状态最多只有 \(16\)。设每个点最多被经过 \(p\) 次,时间复杂度为 \(\mathcal{O}(pnmq)\),其中 \(p\le 16\)。
#include <bits/stdc++.h>
using namespace std;
const int N = 105;
int n, m;
int dr[N][N], dd[N][N];
int getcol(char c) {
if (c == 'P') return 0;
if (c == 'C') return 1;
if (c == 'Z') return 2;
if (c == 'N') return 3;
}
int vis[N][N][20];
struct node {
int x, y, col;
};
queue<node> q;
int num(int x) {
int res = 0;
while (x) {
if (x & 1) res++;
x >>= 1;
}
return res;
}
bool ok(int x, int y) {return x >= 1 && x <= n && y >= 1 && y <= m;}
int bfs(int xa, int ya, int xb, int yb) {
while (!q.empty()) q.pop();
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
for (int k = 0; k < 16; k++) vis[i][j][k] = 0;
}
}
q.push({xa, ya, 0});
while (!q.empty()) {
node f = q.front();
q.pop();
int x = f.x, y = f.y, col = f.col;
if (vis[x][y][col]) continue;
vis[x][y][col] = 1;
if (x == xb && y == yb) continue;
if (ok(x, y + 1) && !vis[x][y + 1][col | (1 << dr[x][y])]) q.push({x, y + 1, col | (1 << dr[x][y])});
if (ok(x + 1, y) && !vis[x + 1][y][col | (1 << dd[x][y])]) q.push({x + 1, y, col | (1 << dd[x][y])});
if (ok(x, y - 1) && !vis[x][y - 1][col | (1 << dr[x][y - 1])]) q.push({x, y - 1, col | (1 << dr[x][y - 1])});
if (ok(x - 1, y) && !vis[x - 1][y][col | (1 << dd[x - 1][y])]) q.push({x - 1, y, col | (1 << dd[x - 1][y])});
}
int ans = 5;
for (int k = 0; k < 16; k++) {
if (vis[xb][yb][k]) {
ans = min(ans, num(k));
}
}
return ans;
}
int main() {
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; i++) {
string s;
cin >> s;
for (int j = 1; j < m; j++) dr[i][j] = getcol(s[j - 1]);
}
for (int i = 1; i < n; i++) {
string s;
cin >> s;
for (int j = 1; j <= m; j++) dd[i][j] = getcol(s[j - 1]);
}
int q;
cin >> q;
while (q--) {
int xa, ya, xb, yb;
cin >> xa >> ya >> xb >> yb;
cout << bfs(xa, ya, xb, yb) << '\n';
}
return 0;
}
C. AN2DL
二维 RMQ 模板题。
如果你学过一维 RMQ 的单调队列做法的话,可以发现把它套在二维问题上,时间复杂度会变成 \(\mathcal{O}(n^3)\)。聪明的你意识到,这是因为每次向队列里加入的元素数量与 \(n\) 同级。但是因为只需要知道最大的,所以可以预处理出每次需要加的是哪个(同样用一维 RMQ 求解),然后复杂度就降低到 \(\mathcal{O}(n^2)+\mathcal{O}(n^2)=\mathcal{O}(n^2)\)。
#include <bits/stdc++.h>
using namespace std;
const int N = 4005;
int n, m, r, s;
int a[N][N], b[N][N], Log2[N];
struct node {
int pos, num;
} q[N * N];
int head, tail;
int main() {
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin >> n >> m;
// for (int i = 2; i < N; i++) Log2[i] = Log2[i >> 1] + 1; // 赛时的废弃代码,没有用
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> a[i][j];
}
}
cin >> r >> s;
// 预处理
for (int j = 1; j <= m; j++) {
head = 0, tail = -1;
for (int i = -r + 2; i + r - 1 <= n; i++) {
while (head <= tail && q[head].pos < i) head++;
while (head <= tail && q[tail].num <= a[i + r - 1][j]) tail--;
q[++tail] = {i + r - 1, a[i + r - 1][j]};
if (j >= 1) b[i][j] = q[head].num;
}
}
// 求解
for (int i = 1; i + r - 1 <= n; i++) {
head = 0, tail = -1;
for (int j = -s + 2; j + s - 1 <= m; j++) {
while (head <= tail && q[head].pos < j) head++;
while (head <= tail && q[tail].num <= b[i][j + s - 1]) tail--;
q[++tail] = {j + s - 1, b[i][j + s - 1]};
if (j >= 1) cout << q[head].num << ' ';
}
cout << '\n';
}
return 0;
}
D. Kocke
首先注意到,很多计数问题都应用 DP 解决。
可以发现在顶端放置积木做起来并不容易,所以将问题倒过来,在下方插入积木。可以设 \(dp[i][j]\) 表示在宽度为 \(i\) 的区间内放置 \(j\) 块积木的方案数。考虑可以从哪里转移:
- 在两侧放置积木。新积木有两种放置方法:左侧和右侧。所以分别从 \(dp[i-1][j-1]\) 和 \(dp[i-1][j-i+1]\) 两个方向转移。
- 在原处放置积木。即在左/右侧放置后又在右/左侧放置。此时从 \(dp[i][j-2]\) 转移。
答案即为方案数乘以放置位置数。也就是 $$\sum_{i=1}^k (dp[i][n]\times (k-i+1))$$。
#include <bits/stdc++.h>
using namespace std;
const int N = 5005, MOD = 1e9 + 7;
int n, k, dp[N][N];
int main() {
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin >> n >> k;
if (n == 1) {
cout << k << '\n';
return 0;
}
if (k == 1) {
cout << 1 << '\n';
return 0;
}
for (int i = 2; i <= n; i++) dp[2][i] = 2;
for (int i = 3; i <= k; i++) {
for (int j = i; j <= n; j++) {
dp[i][j] = ((dp[i - 1][j - 1] + dp[i - 1][j - i + 1]) % MOD + dp[i][j - 2]) % MOD;
}
}
int ans = 0;
for (int i = 1; i <= k; i++) ans = (ans + 1ll * dp[i][n] * (k - i + 1) % MOD) % MOD;
cout << ans << '\n';
return 0;
}
E. Mostovi
待补充。
回顾与反思
这次模拟赛反应了一些问题,如:
- 时间安排不合理
- 对个别知识点的生疏
希望接下来能够避免这些问题。
本文来自博客园,作者:cwkapn,转载请注明原文链接:https://www.cnblogs.com/cwkapn/p/18969019


浙公网安备 33010602011771号