第一次模拟赛总结
第一次摸底考试 总结
考试结果
成绩:\(100+100+80+0+70+0=350\)
排名:#\(18\)
逐题分析
C 钱到题
出现の问题
- 约瑟夫环使用了数组进行维护,取模麻烦,使用
std::queue更为方便
坑点
- 队列
q需要进行初始化
正确代码
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, k;
cin >> n >> k;
queue<int> q; // 维护环
for (int i = 1; i <= n; i ++) q.push(i);
vector<int> w(n + 1, 1); // 记录每人积分
for (int i = 1; i <= k; i ++) {
int x, y;
cin >> x >> y;
while (x--) {
int u = q.front();
q.pop();
if (x == 0) {
w[u] += y;
}
if (w[u]) q.push(u);
else cout << u << " ";
}
}
cout << endl;
return 0;
}
D 浅岛题
出现の问题
- 第二级骗分(
u为v的祖先时,未使用while循环进行修改操作) - 第四级骗分
其实就是正解(思维题:并非LCA,而可以使用数组直接实现op == 1的情况)
根据提示,异或两次等于没有异或,所以
𝑢到𝑣路径上的点的异或和其实没有改变。每次只需要 \(𝑂(1)\) 地修改𝑎[𝑢]和𝑎[𝑣]即可。
坑点
- 无需使用邻接表存图,一个数组即可
vector初始化不使用赋值,使用按位异或
正确代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main() {
ll n = 0, q = 0;
cin >> n >> q;
vector<ll> a(n + 1);
for (int i = 1; i < n; i ++) {
ll u, v, x;
cin >> u >> v >> x;
a[u] ^= x;
a[v] ^= x;
}
for (int i = 1; i <= q; i ++) {
ll op, u, v, x;
cin >> op;
if (op == 1) {
cin >> u >> v >> x;
a[u] ^= x;
a[v] ^= x;
} else {
cin >> u;
cout << a[u] << endl;
}
}
return 0;
}
E 迁到题
出现の问题
- 骗分(\(70pts\))√
- 正解:对于这种最优解题目,没有想到使用动态规划
dp解决。
坑点
dp 状态
使用三维数组,设 \(f(x, y, z)\) 为从起点走到格子 \((x, y)\),使用 \(z\) 个道具所能获得的最大得分。
dp 转移
因为题目要求,每一个格子只可以从上面或者左边转移,所以推出转移方程:
\[f(x, y, z) =
\begin{cases}
max(f(x - 1, y, z, f(x, y - 1, z))) & s_{x, y} = 0 \\
max(f(x - 1, y, z, f(x, y - 1, z))) + 1 & s_{x, y} = 1 \\
max(f(x - 1, y, z), f(x, y - 1, z), f(x, y, z - 1) + 1) & s_{x, y} = ? \\
\end{cases}
\]
- 注意:第三行的 \(f(x, y, z - 1) + 1\) 尤为关键。(消耗一次魔法使用问号的机会)
- 注意:
z一定要从大到小遍历! - 注意:
z的枚举范围:for (int z = k; z >= 0; z --)
正确代码
#include <bits/stdc++.h>
using namespace std;
int n, m, k;
char arr[105][105];
long long f[105][105][105]; // 设 dp(x, y, z) 为从起点走到格子 (x, y),使用 z 个道具所能获得的最大得分。
int main() {
cin >> n >> m >> k;
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= m; j ++) {
cin >> arr[i][j];
}
}
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= m; j ++) {
for (int l = k; l >= 0; l --) {
f[i][j][l] = max(f[i - 1][j][l], f[i][j - 1][l]);
if (arr[i][j] == '1') {
f[i][j][l] ++;
} else if (arr[i][j] == '?' && l != k) {
f[i][j][l + 1] = max(f[i][j][l + 1], f[i][j][l] + 1);
}
}
}
}
cout << f[n][m][k] << endl;
return 0;
}
F 千刀题
出现の问题
- 二分:因为边随着时间的流逝不会消失,只会增加,所以当 \(T'\) 满足要求时,所有 \(> T'\) 的答案也必定符合要求。
六种解法及获得分数(难度递增)
- \(m=8\) 暴搜每条边的出现情况,找到出现最晚的边,判断(\(20pts\))
- \(t\leq n \leq100\) 枚举答案 \(T\),判断(\(60pts\))
- \(s=1\) 二分答案 + 暴力
bfs检验(\(70pts\)) - \(v=1\) 双向
bfs优化(\(80pts\)) - 正解:只关心
V中的点是否能被S中的点到达,不关心是哪一个点,所以初始时把所有S中的点都加入bfs队列
坑点
- \(T=0\) 时,可能符合要求
- 不保证
S和V无交集 vis数组和ver邻接表需要是全局变量,不可以开在函数内
正确代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
const int M = 1e6 + 10;
int n, m, S, V;
struct Node {
int u, v, t;
} arr[M];
int s[N], v[N];
bool vis[N];
vector<int> ver[N];
bool check(int x) {
memset(vis, 0, sizeof vis);
for (int i = 1; i <= n; i ++) {
ver[i].clear();
}
for (int i = 1; i <= m; i ++) {
if (arr[i].t <= x) {
ver[arr[i].u].push_back(arr[i].v);
ver[arr[i].v].push_back(arr[i].u);
}
}
queue<int> q;
for (int i = 1; i <= S; i ++) {
q.push(s[i]);
vis[s[i]] = true;
}
while (!q.empty()) {
int tmp = q.front();
q.pop();
for (auto i: ver[tmp]) {
if (!vis[i]) {
q.push(i);
vis[i] = true;
}
}
}
for (int i = 1; i <= V; i ++) {
if (!vis[v[i]]) {
return false;
}
}
return true;
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i ++) {
scanf("%d%d%d", &arr[i].u, &arr[i].v, &arr[i].t);
}
scanf("%d", &S);
for (int i = 1; i <= S; i ++) {
scanf("%d", &s[i]);
}
scanf("%d", &V);
for (int i = 1; i <= V; i ++) {
scanf("%d", &v[i]);
}
int l = 0, r = 1e9, ans = 0;
while (l <= r) {
int mid = (l + r) >> 1;
if (check(mid)) {
ans = mid;
r = mid - 1;
} else {
l = mid + 1;
}
}
cout << ans << endl;
return 0;
}

浙公网安备 33010602011771号