2025.6.1
刷题日记
累了。
手感奇差,也别管什么1600不1600的了,先把最近的题补一补再说吧
又菜又爱玩了属于是
Codeforces Round 1028 (Div.2) B. Gellyfish and Baby's Breath
https://codeforces.com/contest/2116/problem/B
这道题给定两个排列p和q,要求计算一个数组r
其中ri = max(2 ^ p[j] + 2 ^ q[i - j]),其中j ∈ [0, i]
我们可以知道,对于正整数a和b,2 ^ a + 2 ^ b的值取决于a和b中的较大者
至于小的那个是多少,是可以忽略不计的,举个例子
2 ^ 6 + 2 ^ 4 的选择肯定优于 2 ^ 5 + 2 ^ 5 的选择
因此,我们在这道题中,对于第i位,去检查p的前i位中最大值以及q的前i位中最大值,记录其下标为j,再去另一边找对应的i - j位,比较一下
值得注意的是,我们不应该比较(2 ^ p[j] + 2 ^ q[i - j]) mod 998244353后的结果,因为模后最大不一定原数最大
因此我们直接比较这里的p[i]、q[i - j]这些数字的大小
实现上述思路需要我们用到前缀最大值,代码如下
#include <bits/stdc++.h>
using ll = int64_t;
const ll N = 2e5 + 10;
const ll INF = LLONG_MAX;
const ll mod = 998244353;
ll fastpow(ll base, ll power) {
ll ans = 1;
while (power) {
if (power & 1) ans = ans * base % mod;
base = base * base % mod;
power >>= 1;
}
return (ans + mod) % mod;
}
void solve () {
int n;
std::cin >> n;
std::vector<ll> p(n), q(n);
std::vector<ll> pre_p(n), pre_q(n);
std::unordered_map<ll, ll> st1, st2;
for (int i = 0; i < n; i++) {
std::cin >> p[i];
if (i == 0) {
st1[p[i]] = i;
pre_p[i] = p[i];
}
else if (i > 0) {
if (pre_p[i - 1] < p[i]) {
st1[p[i]] = i;
pre_p[i] = p[i];
}
else {
pre_p[i] = pre_p[i - 1];
}
}
}
for (int i = 0; i < n; i++) {
std::cin >> q[i];
if (i == 0) {
st2[q[i]] = i;
pre_q[i] = q[i];
}
else if (i > 0) {
if (pre_q[i - 1] < q[i]) {
st2[q[i]] = i;
pre_q[i] = q[i];
}
else {
pre_q[i] = pre_q[i - 1];
}
}
}
for (int i = 0; i < n; i++) {
ll j = st1[pre_p[i]];
ll tmp1 = (fastpow(2, pre_p[i]) + fastpow(2, q[i - j])) % mod;
ll k = st2[pre_q[i]];
ll tmp2 = (fastpow(2, pre_q[i]) + fastpow(2, p[i - k])) % mod;
ll ans;
if (pre_p[i] > pre_q[i]) {
ans = tmp1;
}
else if (pre_p[i] < pre_q[i]) {
ans = tmp2;
}
else {
if (pre_p[i] + q[i - j] >= pre_q[i] + p[i - k]) {
ans = tmp1;
}
else {
ans = tmp2;
}
}
std::cout << ans << " \n"[i == n - 1];
}
}
int main () {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t = 1;
std::cin >> t;
while (t--) {
solve();
}
return 0;
}
Codeforces Round 1026 (Div.2) C. Racing
https://codeforces.com/contest/2110/problem/C
给定n个操作和n个飞机可穿梭的洞的上下界,让你判断飞机能否通过
1代表上升1格,0代表不动,-1代表可以修改为上升或不变,飞机初始高度是0
一开始用dfs来着,然后TLE了
这道题的正解用了一个栈来存放可以修改操作的点的序号
定义一个h表示飞机高度,除了一开始被标为-1的点,飞机的高度都加上当前操作(0或1)
如果在运算过程中,有 h + st.size() < l[i] 或者 h > r[i]
那么就意味着,这个点无论如何也飞不过去,这时输出-1并return即可
如果理论上可以飞过,但现在过不去,那么就开始依次的修改栈内存放下标所对应的原数组的值
在能飞过去之前,一直执行d[dt.top()] = 1, h++, st.pop()这样的操作
在运算过程结束后,不要忘记将栈中剩余的下标对应的数值改为0,代码如下
#include <bits/stdc++.h>
using ll = int64_t;
const ll N = 2e5 + 10;
const ll INF = LLONG_MAX;
const ll mod = 998244353;
void solve () {
#define l first
#define r second
int n;
std::cin >> n;
std::vector<int> d(n + 1, 0);
std::vector<std::pair<int, int>> a(n + 1);
std::stack<int> st;
for (int i = 1; i <= n; i++) {
std::cin >> d[i];
}
for (int i = 1; i <= n; i++) {
std::cin >> a[i].l >> a[i].r;
}
int height = 0;
for (int i = 1; i <= n; i++) {
if (d[i] != -1) height += d[i];
else st.push(i);
if (height + st.size() < a[i].l || height > a[i].r) {
std::cout << "-1\n";
return;
}
while (st.size() && height < a[i].l) {
d[st.top()] = 1;
height++;
st.pop();
}
while (height + st.size() > a[i].r) {
d[st.top()] = 0;
st.pop();
}
}
while (st.size()) {
d[st.top()] = 0;
st.pop();
}
for (int i = 1; i <= n; i++) {
std::cout << d[i] << " \n"[i == n];
}
}
int main () {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t = 1;
std::cin >> t;
while (t--) {
solve();
}
return 0;
}
Codeforces Round 1026 (Div.2) D. Fewer Batteries
https://codeforces.com/contest/2110/problem/D
给定一个DAG(有向无环图),每个路径有所需的电池数量,如果当前电池数量大于路径所需即可通行
每个节点也有权值,意为在这个节点最多可以增加的电池数量,求起点到终点所需最小电池数量
一开始以为是Dijkstra,后来发现应该是bfs,不过都WA了。正解是二分 + dp
开一个数组f记录,f[i]意为到达i点所需最大电池数量,然后二分去枚举每一个起点,直到f[i]取得最小值
#include <bits/stdc++.h>
using ll = int64_t;
const ll N = 2e5 + 10;
const ll INF = LLONG_MAX;
const ll mod = 998244353;
typedef struct node {
int dot, bat;
} node;
void solve () {
int n, m;
std::cin >> n >> m;
std::vector<int> b(n + 1);
std::vector<node> graph[n + 1];
std::vector<int> ind(n + 1, 0);
for (int i = 1; i <= n; i++) {
std::cin >> b[i];
}
for (int i = 1; i <= m; i++) {
int u, v, w;
std::cin >> u >> v >> w;
graph[u].push_back({v, w});
ind[v]++;
}
auto check = [&] (int mid) {
std::vector<int> f(n + 1, -1);
f[1] = std::min(mid, b[1]);
for (int now = 1; now <= n; now++) {
for (auto [next, bat] : graph[now]) {
if (f[now] >= bat) {
f[next] = std::max(f[next], std::min(mid, f[now] + b[next]));
}
}
}
return f[n] != -1 ? 1 : 0;
};
int l = 0, r = 1e9 + 10, ans = -1;
while (l <= r) {
int mid = l + r >> 1;
if (check(mid)) {
ans = mid;
r = mid - 1;
} else {
l = mid + 1;
}
}
std::cout << ans << '\n';
}
int main () {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t = 1;
std::cin >> t;
while (t--) {
solve();
}
return 0;
}
Codeforces Round 1028 (Div.2) C. Gellyfish and Flaming Peony
https://codeforces.com/contest/2116/problem/C
给定一个数组a,仅能使用以下一种操作,使得数组a中所有元素都相等,并最小化操作的次数
选择i,j,用gcd(a[i], a[j])替换a[i]
那么显然,我们这样子操作,最后数组a中的元素一定为gcd(a[1], a[2], a[3], ……, a[n - 1], a[n])
这道题的思路很简单,总次数最小的方式就是先最快的让数组a其中一个元素等于总gcd,然后对于不等的元素挨个gcd一下就可以了!
此时博主开始了他的dfs(最近很没感觉,什么题都想不到好的思路,只能想到暴力的思路,于是这几天很热衷写dfs,明知道会超时但还是想写)
#include <bits/stdc++.h>
using ll = int64_t;
const ll N = 2e5 + 10;
const ll INF = LLONG_MAX;
const ll mod = 998244353;
void solve () {
int n;
cin >> n;
std::vector<int> a(n + 1);
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
int GCD = a[1];
for (int i = 1; i <= n; i++) {
GCD = std::__gcd(GCD, a[i]);
}
int res = 0x3f3f3f3f, ans = 0;
std::vector<int> vis(n + 1, 0);
std::function<void(int, int)> dfs = [&] (int u, int step) {
if (u == GCD) {
res = std::min(res, step);
return;
}
for (int i = 1; i <= n; i++) {
if (!vis[i]) {
vis[i] = 1;
dfs(std::__gcd(u, a[i]), step + 1);
vis[i] = 0;
}
}
return;
};
dfs(a[0], -1);
vis[0] = 1;
for (int i = 1; i <= n; i++) {
if (a[i] != GCD) ans++;
}
if (ans == n)
std::cout << ans - 1 + res << '\n';
else
std::cout << ans + res << '\n';
}
int main () {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t = 1;
cin >> t;
while (t--) {
solve();
}
return 0;
}
直接超时
后来问了一下ai,ai说用bfs,然后就试了一下bfs,然后真TM过了woc
代码
#include <bits/stdc++.h>
using ll = int64_t;
const ll N = 2e5 + 10;
const ll INF = LLONG_MAX;
const ll mod = 998244353;
void solve () {
int n;
std::cin >> n;
std::vector<int> a(n + 1);
for (int i = 1; i <= n; i++) {
std::cin >> a[i];
}
int GCD = a[1];
for (int i = 1; i <= n; i++) {
GCD = std::__gcd(GCD, a[i]);
}
std::queue<std::pair<int, int>> q;
std::unordered_map<int, int> vis;
int res = n, ans = 0, flag = 0;
for (int i = 1; i <= n; i++) {
if (a[i] == GCD) {
flag = 1;
break;
}
}
if (flag) {
res = 0;
}
else {
for (int i = 1; i <= n; i++) {
q.push({a[i], 0});
vis[a[i]] = 1;
}
while (q.size()) {
auto [now, step] = q.front();
q.pop();
if (now == GCD) {
res = std::min(res, step);
continue;
}
for (int i = 1; i <= n; i++) {
int new_gcd = std::__gcd(now, a[i]);
if (!vis[new_gcd] || step + 1 < vis[new_gcd]) {
vis[new_gcd] = step + 1;
q.push({new_gcd, step + 1});
}
}
}
}
for (int i = 1; i <= n; i++) {
if (a[i] != GCD) ans++;
}
if (ans == n)
std::cout << ans - 1 + res << '\n';
else
std::cout << ans + res << '\n';
}
int main () {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t = 1;
cin >> t;
while (t--) {
solve();
}
return 0;
}
不过博主还是不满意,毕竟这个感觉像是混过去的
然后想到了之前学dp时候的看到的一句话:
DFS -> memo-DFS -> DP
dfs可以优化成记忆化搜索,记忆化搜索可以进一步优化成dp
然后博主试着去优化我的dfs……
硬刚了半个下午,写出了一个dp(本人是弱弱,不会写)
#include <bits/stdc++.h>
using ll = int64_t;
const ll N = 2e5 + 10;
const ll INF = LLONG_MAX;
const ll mod = 998244353;
void solve () {
int n;
std::cin >> n;
std::vector<int> a(n + 1);
for (int i = 1; i <= n; i++) {
std::cin >> a[i];
}
int res = n, ans = 0;
int GCD = a[1], flag = 0;
int max = *max_element(a.begin() + 1, a.end());
for (int i = 1; i <= n; i++) {
GCD = std::__gcd(GCD, a[i]);
}
for (int i = 1; i <= n; i++) {
if (a[i] == GCD) flag = 1;
else ans++;
}
if (!flag) {
std::vector<int> f(10000, 0x3f3f3f3f);
for (int i = 1; i <= n; i++) {
f[a[i]] = 0;
}
for (int j = 1; j <= n; j++) {
for (int i = 1; i <= max; i++) {
f[std::__gcd(i, a[j])] = std::min(f[std::__gcd(i, a[j])], f[i] + 1);
}
}
res = f[GCD];
}
else {
res = 0;
}
if (ans == n)
std::cout << ans - 1 + res << '\n';
else
std::cout << ans + res << '\n';
}
int main () {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t = 1;
std::cin >> t;
while (t--) {
solve();
}
return 0;
}
不过还是TLE了
这道题数据很有意思,数组长度 <= 5000,数组中元素的大小也不超过5000
感觉很像是dp,但到现在也不知道为什么TLE
算了,就这样吧,至少相比于之前对dp闻风丧胆,这次至少踏出了第一步了
事已至此,先祝自己六一快乐吧(