Edu 118(Div.2)
A. Long Comparison
题意
给定四个数字 $x, p_1, y, p_2$ ,$p_1$ 表示 $x$ 后面的 $0$ 个数, $p_2$ 表示 $y$ 后面的 $0$ 个数,比较 $x, y$ 的大小。
分析
- 位数相同:根据位数判断。
- 位数不同:把 $x$ 和 $y$ 的后导 $0$ 去掉,再比较 $x, y$ 。
Code
/* 终点是一切概率的结束,也是一切期望的开始 */
#include <bits/stdc++.h>
using namespace std;
void solve ()
{
string a; int p; cin >> a >> p;
string b; int p2; cin >> b >> p2;
if (a.size() + p != b.size() + p2)
{
if (a.size() + p > b.size() + p2) cout << '>' << endl;
else cout << '<' << endl;
}
else
{
while(a[a.size() - 1] == '0') p ++ , a = a.substr(0, a.size() - 1);
while(b[b.size() - 1] == '0') p2 ++, b = b.substr(0, b.size() - 1);
if (a == b) cout << '=' << endl;
else if (a > b) cout << '>' << endl;
else cout << '<' << endl;
}
}
signed main ()
{
cout.tie(0)->sync_with_stdio(0);
int _; for (cin >> _; _--; ) solve();
return 0;
}
B. Absent Remainder
题意
给定长度为 $n$ 的序列 $a$,找出 $\lfloor \dfrac n 2 \rfloor$ 个序偶 $(x, y)$ 满足:
- $x != y$ 。
- $x \in a, y \in a$ 。
- $x \ \ mod \ \ y \notin a$ 。
分析
对序列排序,输出后面 $\lfloor \dfrac n 2 \rfloor$ 和 $a_1$ 组成的序偶即可,因为模数一定小于 $a_1$ ,而 $a_1$ 是序列最小的数字,因此模数一定不存在在 $a$ 中。
Code
/* 终点是一切概率的结束,也是一切期望的开始 */
#include <bits/stdc++.h>
#define rep(i, x, y) for(int i = x; i <= y; i++)
using namespace std;
const int N = 200010;
int a[N];
void solve ()
{
int n; cin >> n;
rep(i, 1, n) cin >> a[i];
sort(a + 1, a + n + 1);
for (int i = 0, p = 2; i < n / 2; i ++, p ++ )
cout << a[p] << ' ' << a[1] << endl;
}
signed main ()
{
cout.tie(0)->sync_with_stdio(0);
int _; for (cin >> _; _--; ) solve();
return 0;
}
C. Poisoned Dagger
题意
敌人有 $h$ 血量,你有 $n$ 次攻击,第 $i$ 次攻击会从 $a_i$ 秒开始,每秒对敌人造成 $1$ 血量,同时前面的攻击无效(即每秒只会造成 $1$ 伤害),攻击有 $k$ 秒的延长时间,即如果从 $a_i$ 开始攻击,敌人会在 $a_i, a_{i+1} \ldots a_{i+k-1}$ 秒受到伤害。问 $k$ 的最小值。
分析
每次攻击会造成 $min(k, d_i)$ 伤害 ($d_i$ 为两次攻击的间隔)。
由于答案有单调性,可以二分枚举 $k$ 的值。
Code
/* 终点是一切概率的结束,也是一切期望的开始 */
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 200010;
int d[N]; // 间隔,n-1个
int n, h;
bool check (int mid)
{
int m = 0;
for (int i = 1; i < n; i ++ )
m += min(d[i], mid);
m += mid;
return m >= h;
}
void solve ()
{
cin >> n >> h;
int last; cin >> last;
for (int i = 1; i < n; i ++ )
{
int x; cin >> x;
d[i] = x - last;
last = x;
}
int l = 1, r = 1e18 + 10;
while(l < r)
{
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
cout << r << endl;
}
signed main ()
{
cout.tie(0)->sync_with_stdio(0);
int _; for (cin >> _; _--; ) solve();
return 0;
}
D. MEX Sequences
题意
定义序列为 $MEX-correct$ 当且仅当序列满足:$|x_i - Mex(x_1, x_2 \ldots x_i)| \le 1$ 。
给定长度为 $n$ 的序列$a$ ,问序列 $a$ 有多少子序列为 $MEX-correct$ 。
分析
参考了嘉心糖的代码
当一个元素为 $x$ 时,它的 $mex$ 只能为 $x-1$ 或者 $x+1$ 。
假设序列某一个前缀 $mex$ 为 $x$ ,那么这个前缀序列的最大值只能为 $x-1$ 或 $x+1$ 。
所以对于某一个元素 $x$ ,可以对$MEX-correct$分为四类:
- $mex = x-1, max = x$ 。
- $mex = x-1, max = x-2$ 。
- $mex = x+1, max = x+2$ 。
- $mex = x + 1, max = x$ 。
假设 $(x, y)$ 表示 $(mex, max)$ 。
我们设 $dp1(i)$ 表示序列性质为 $(i, i-1)$ 的序列数量,$dp2(i)$ 表示 $(i, i+1)$ 的序列数量。
对于 $(x-1, x)$ 而言,由于当前元素为 $x$ ,我们可以选择把元素加或者不加入 $(x-1, x)$ 序列,不会影响 $(x-1, x)$ 序列性质。
同时可以把 $x$ 加入到 $(x-1, x-2)$ 序列中使得它变为 $(x-1, x)$ 。但是这个对于 $x = 1$ 时,$(0, -1)$ 一定方案数为 $0$ ,但是我们可以选择直接选择 $\{1\}$ ,这个也满足 $(x-1, x)$ ,贡献为 $1$。
所以 $dp2(x-1) = dp2(x-1) \times 2 + dp1(x-1) + (x == 1)$ 。
对于 $(x-1, x-2)$ ,它是没有转移的。
对于 $(x+1, x+2)$ ,由于 $mex = x+1$ ,所以序列里一定有 $x$ ,加入 $x$ 不会影响序列性质。
所以 $dp2(x+1) = dp2(x+1) \times 2$ 。
对于 $(x+1, x)$ ,可以选择加入 $x$ 不改变性质,也可以把 $x$ 加入 $(x, x-1)$ 转移为 $(x+1, x)$ 。
注意当 $x = 0$ 时 $(0, -1)$ 一定为 $0$ 个。但是可以直接选择 $\{0\}$ ,满足 $(x+1, x)$ ,贡献为 $1$。
所以 $dp1(x+1) = dp1(x+1) \times 2 + dp1(x) + (x == 0)$ 。
根据 $dp1, dp2$ 的定义,把所有方案加起来就是总方案。
Code
/* 终点是一切概率的结束,也是一切期望的开始 */
#include <bits/stdc++.h>
#define rep(i, x, y) for(int i = x; i <= y; i++)
#define int long long
using namespace std;
const int mod = 998244353;
/*
* dp1(i) 表示mex为i且最大值为i-1的方案数量
* dp2(i) 表示mex为i且最大值为i+1的方案数量
*/
void solve ()
{
int n, ret = 0; cin >> n;
map<int, int> dp1, dp2;
rep(i, 1, n)
{
int x; cin >> x;
dp2[x-1] = (dp2[x-1] * 2 + dp1[x-1] + (x == 1)) % mod;
dp2[x+1] = (dp2[x+1] * 2) % mod;
dp1[x+1] = (dp1[x+1] * 2 + dp1[x] + (x == 0)) % mod;
}
for (auto & [k, v] : dp1) ret = (ret + v) % mod;
for (auto & [k, v] : dp2) ret = (ret + v) % mod;
cout << ret << endl;
}
signed main ()
{
cout.tie(0)->sync_with_stdio(0);
int _; for (cin >> _; _--; ) solve();
return 0;
}
E. Crazy Robot
题意
给定矩阵图,上面有 $L$ 表示实验室, $\#$ 表示障碍物,$.$ 表示空地。
有一个机器人可能在图上的任意一个空地上,你可以给它任意下达方向指令,它会选择一个与指令不同的方向且该方向不为障碍物,向这个方向走,否则不会移动。
问有多少可能的空地,使得机器人在该点时,无论机器人如何行动,总能选择指令使它到达实验室。
分析
从实验室出发,如果机器人只有一个方向有空地,则我们可以选择走这个空地的指令,那么机器人一定会向实验室走。
注意最后输出地图不要使用 $endl$ ,否则输出 $10^6$ 次会超时。
Code
/* 终点是一切概率的结束,也是一切期望的开始 */
#include <bits/stdc++.h>
#define rep(i, x, y) for(int i = x; i <= y; i++)
using namespace std; using PII = pair<int, int>;
const int N = 1000010;
const int dr[] = {-1, 1, 0, 0}, dc[] = {0, 0, -1, 1};
PII q[N];
void solve ()
{
int n, m, x, y; cin >> n >> m;
vector g(n, vector<char>(m));
rep(i, 0, n-1) rep(j, 0, m-1)
{
cin >> g[i][j];
if (g[i][j] == 'L') x = i, y = j;
}
int hh = 0, tt = -1;
// 把周围点加进来
for (int i = 0; i < 4; i ++ )
{
int dx = x + dr[i], dy = y + dc[i];
if (dx < 0 || dx >= n || dy < 0 || dy >= m || g[dx][dy] == '#') continue;
q[++ tt] = {dx, dy};
}
while(hh <= tt)
{
PII t = q[hh ++ ];
int x = t.first, y = t.second, cnt_free = 0;
for (int i = 0; i < 4; i ++ )
{
int dx = x + dr[i], dy = y + dc[i];
if (dx < 0 || dx >= n || dy < 0 || dy >= m) continue;
if (g[dx][dy] == '.') cnt_free ++ ;
}
if (cnt_free > 1) continue;
g[x][y] = '+';
for (int i = 0; i < 4; i ++ )
{
int dx = x + dr[i], dy = y + dc[i];
if (dx < 0 || dx >= n || dy < 0 || dy >= m) continue;
if (g[dx][dy] == '.') q[++ tt] = {dx, dy};
}
}
rep(i, 0, n-1)
{
rep(j, 0, m-1) cout << g[i][j];
cout << '\n';
}
}
signed main ()
{
cout.tie(0)->sync_with_stdio(0);
int _; for (cin >> _; _--; )
solve();
return 0;
}
巨巨 D题的描述的状态转移 不是有问题?
哪里有问题呢
dp1 和 dp2 是不是写反了。。
确实hh
赞 hh