当前位置: 首页 > news >正文

9 ABC408 D~F 题解

ABC408 D~F 题解

D

题面

给定一个长度为 \(n\) 的由 01 组成的字符串 \(S\),每次操作可以将某个 0 改成 1,或者将某个 1 改成 0 。

求字符串中至多有一个连续 1 串的最小操作次数。

题解

解法1

考场思路,将每个连续 1 串挑出来,考虑每个 1 串选或者不选。

\(f(i,0/1)\) 表示第 \(i\) 个 1 串选/不选的最小操作次数,分别考虑如何转移。

如果不选,那么不需要将两个连续 1 串中间的 0 变为 1,所以新增操作次数即为当前 1 串的长度。

如果选,那么要和前面最后一个 1 串连起来,或者自己成为第一个 1 串。

有转移:

\[\begin{align*} &f(i, 0) = \min \{ f(i - 1, 0), f(i - 1, 1) \} + len_i \\ &f(i, 1) = \min \{ \min_{1 \le j < i} \{ f(j, 1) - t_j\} + h_i - 1 , sum_{i - 1}\} \end{align*} \]

其中 \(h,t\) 表示一个 1 串的头、尾坐标,\(sum_i\) 表示前 \(i\) 个 1 串的长度和。

解法2

\(sum_i\) 表示前 \(i\) 个位置中 1 的个数。

假设我们枚举了最后 1 串的左右端点 \(l,r\) ,那么答案应该怎么算?

不难得出为 \(2sum_{l - 1} - l + r- 2sum_r + sum_n + 1\)

\(a_i = 2sum_{i - 1} - i, b_i = i - 2sum_i + sum_n + 1\),那么刚才的答案也就能够写成 \(a_l + b_r\)

假设我们固定了一个右端点,我们也就是要找到 \(a_l\) 最小的 \(l\) ,从而用 \(a_l + b_r\) 来更新答案。

我们完全可以用一个变量来维护最小 \(a_l\) ,然后右端点不断向右扫。

code

解法1

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>using namespace std;namespace michaele {const int N = 2e5 + 10;int n, cnt;int f[N][2], sum[N], st[N], top;char s[N];struct seg {int h, t, len;} a[N];void solve () {cnt = 0;cin >> n;scanf ("%s", s + 1);int p = 1;while (p <= n) {if (s[p] == '0') {p ++;continue;}int h = p;while (s[p] == '1' && p <= n) p ++;a[ ++ cnt] = {h, p - 1, p - h};sum[cnt] = sum[cnt - 1] + p - h;}if (cnt == 0) {cout << 0 << endl;return;}memset (f, 0x3f, sizeof f);f[1][0] = a[1].len;f[1][1] = 0;st[1] = 1, top = 1;for (int i = 2; i <= cnt; i ++) {f[i][0] = min (f[i - 1][0], f[i - 1][1]) + a[i].len;int j = st[top];f[i][1] = min (f[j][1] + a[i].h - a[j].t - 1, sum[i - 1]);while (top && f[i][1] - a[i].t <= f[st[top]][1] - a[st[top]].t) top --;st[ ++ top] = i;}cout << min (f[cnt][0], f[cnt][1]) << endl;}void Main () {int T;cin >> T;while (T --) {solve ();}}
}int main () {// freopen ("test/test.in", "r", stdin);// freopen ("test/test.out", "w", stdout);michaele :: Main ();return 0;
}

解法2

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <deque>using namespace std;namespace michaele {const int N = 2e5 + 10, M = N << 1;int n, sum[N], a[N], b[N];char s[N];void solve () {cin >> n;scanf ("%s", s + 1);for (int i = 1; i <= n; i ++) {sum[i] = sum[i - 1] + (s[i] == '1');}if (sum[n] == 0) {cout << 0 << endl;return;}for (int i = 1; i <= n; i ++) {a[i] = 2 * sum[i - 1] - i;b[i] = i - 2 * sum[i] + 1 + sum[n];}int ans = 2e9, mn = 2e9;for (int i = 1; i <= n; i ++) {mn = min (mn, a[i]);ans = min (ans, mn + b[i]);}cout << ans << endl;}void Main () {int T;cin >> T;while (T --) {solve ();}}
}int main () {michaele :: Main ();return 0;
}

E

题面

给定一张 \(n\) 个点,\(m\) 条边的无向连通图,每条边有边权 \(w_i\)

定义某条路径的长度为路径上的边权的异或和,求从 1 到 n 的最短路径长度。

\(2 \le n \le 2 \times 10^5, n - 1 \le m \le 2 \times 10^5\)

\(0 \le w_i \le 2^{30}\)

题解

这种位运算的题,一般来说都要将每一位分开考虑。

这里我们是要求一个从 1 到 n 的最短或路径,我们从高到低考虑每一位。

如果这一位能选 0,那么一定选 0,否则只能选 1。

如何判断这一位能不能选 0 ?

我们可以将边权变为这一位的值,然后跑个 01BFS,看最后 \(dis_n\) 是否为 0 即可。如果这一位能选 0 ,那么要将所有这一位为 1 的边删除,保证后面的选择是在这一位为 0 的前提下的。

时间复杂度 \(O(n \log n)\)

code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <deque>using namespace std;namespace michaele {const int N = 2e5 + 10, M = N << 1;int n, m;int h[N], ver[M], ne[M], e[M], tot;bool del[M];void add (int x, int y, int z) {ver[ ++ tot] = y;ne[tot] = h[x];h[x] = tot;e[tot] = z;}int dis[N];void bfs (int bit) {memset (dis, 0x3f, sizeof dis);deque <int> q;dis[1] = 0;q.push_front (1);while (q.size ()) {int x = q.front ();q.pop_front ();for (int i = h[x]; i; i = ne[i]) {if (del[i]) continue;int y = ver[i], z = (e[i] >> bit) & 1;if (dis[y] > (dis[x] | z)) {dis[y] = (dis[x] | z);if (z == 0) q.push_front (y);else q.push_back (y);}}}}void solve () {cin >> n >> m;for (int i = 1; i <= m; i ++) {int x, y, z;cin >> x >> y >> z;add (x, y, z);add (y, x, z);}int ans = 0;for (int i = 30; i >= 0; i --) {bfs (i);if (dis[n] != 0) ans += (1 << i);else {for (int j = 1; j <= tot; j ++) {if ((e[j] >> i) & 1) {del[j] = 1;}}}}cout << ans << endl;}
}int main () {michaele :: solve ();return 0;
}

F

题面

\(n\) 个木桩,第 \(i\) 个木桩的高度为 \(H_i\)

小乐要做个游戏。他将任意选一个木桩为起点,然后不断移动到下一个木桩,从木桩 \(i\) 可以移动到木桩 \(j\) 当且仅当 \(H_j \le H_i - D \and|i - j| \le R\)

求他在游戏中最多可以移动多少次。

\(1 \le D, R \le n \le 5 \times 10^5\)

\(H\)\(1 \sim n\) 的排列

题解

这道题转移有两个限制,应该算个 trick。

如果单个限制,很好做,第一个维护一个 \(set\),第二个维护线段树。

但这两个合到一起,我们可以从小到大枚举 \(H_i\),然后将 \(H_i - D\) 对应位置的 \(f\) 单点修改到线段树中,然后每次在线段树上查询对应区间的最大值更新 \(H_i\) 对应位置的 \(f\) 即可。

时间复杂度 \(O(n \log n)\)

code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <deque>using namespace std;namespace michaele {#define ls (p << 1)#define rs (p << 1 | 1)const int N = 5e5 + 10, M = N << 1;int n, D, R;int a[N], id[N], f[N];int val[N << 2];void update (int p) {val[p] = max (val[ls], val[rs]);}void modify (int p, int l, int r, int pos, int d) {if (l == r) {val[p] = d;return;}int mid = (l + r) >> 1;if (pos <= mid) modify (ls, l, mid, pos, d);else modify (rs, mid + 1, r, pos, d);update (p);}int query (int p, int l, int r, int x, int y) {if (x <= l && r <= y) {return val[p];}int mid = (l + r) >> 1;int res = -1;if (x <= mid) res = max (res, query (ls, l, mid, x, y));if (mid < y) res = max (res, query (rs, mid + 1, r, x, y));return res; }void solve () {cin >> n >> D >> R;for (int i = 1; i <= n; i ++) {cin >> a[i];id[a[i]] = i;}memset (val, -1, sizeof val);int ans = 0;for (int i = 1; i <= n; i ++) {int pos = id[i];if (i > D) {modify (1, 1, n, id[i - D], f[id[i - D]]);int tmp = query (1, 1, n, pos - R, pos + R); if (tmp == -1) f[pos] = 0;else f[pos] = tmp + 1;} else {f[pos] = 0;}ans = max (ans, f[pos]);}cout << ans << endl;}
}int main () {michaele :: solve ();return 0;
}
http://www.hskmm.com/?act=detail&tid=28264

相关文章:

  • 8 ABC425 G 题解
  • 智能防御,安全赋能:AI-FOCUS 滤海AI DLP 化解外部 AI 风险
  • IDEA快捷键
  • VS code 中代码补全 自动补全函数括号
  • 优先队列
  • 学习ReAct并使用langgraph实现一个简单的ReAct AI Agent!!
  • abc 408 d~f
  • RMQ与LCA学习笔记
  • mamba-硬件感知算法
  • Java1
  • 完整教程:lua代码解析1
  • 二维数点
  • gitee和github如何修改仓库名并且保持与原远程仓库的连接?(手把手教学) - 实践
  • 2025.10.10总结 - A
  • [20251010]建立完善tpt的prr.sql脚本.txt
  • 第十一篇
  • testtest
  • 题解:AT_arc138_f [ARC138F] KD Tree
  • SP33 TRIP - Trip 个人题解
  • 经营不是老板一个人的事 - 智慧园区
  • Codeforces Round 1051 (Div. 2)[A ~E]
  • 如何在 Spring Boot 应用中配置多个 Spring AI 的 LLM 客户端
  • 使用eBPF技术保护FastAPI安全
  • 项目案例作业2:对案例进行面向对象分析
  • JavaSE
  • CF2064E Mycraft Sand Sort
  • Servlet笔记
  • 第一个博客
  • k8s 主节点重启后 从节点 get 异常 - 教程
  • 多维索引技术优化数据湖查询性能