ABC 369
刚才翻上次写的 abc 366 题解, 发现语言挺抽象, 导致自己都快看不懂了, 这回写好点
这段时间第一次 Rated, 情况一般吧, F 忘给同一个
369 这数挺吉利哈哈, 济南好像有说法是初三初六初九宜出行, 济南公交也有个 app 叫 369。这次就当作重新学 oi 的开始了, 369, 宜踏上新的征程!
A
题意
给定
解答
直接分类讨论即可, 重点是
更简单的写法是直接枚举
代码
#include<bits/stdc++.h> #define int long long using namespace std; const int N = 2e5 + 10, MOD = 998244353; int a, b; int ans; signed main(){ // freopen("1.in", "r", stdin); cin >> a >> b; ++ans; if((a + b) / 2 != a && (a + b) / 2 != b && (a + b) % 2 == 0) ++ans; if(a != b) ++ans; cout << ans << "\n"; }
B
题意
弹钢琴, 左右手相互独立, 按顺序给出每一步左/右手需要到达的位置坐标。
定义疲劳度为两坐标的差的绝对值, 手的起始位置由你决定, 求最小疲劳度
解答
这题有点诈骗啊
首先每步操作按顺序给出, 不能改变
然后起始位置设成左/右手分别的第一步操作的坐标即可, 剩下的是语法组内容
代码
#include<bits/stdc++.h> #define int long long using namespace std; const int N = 2e5 + 10, MOD = 998244353; int n, v; char opt; vector <int> l, r; signed main(){ // freopen("1.in", "r", stdin); cin >> n; for(int i = 1; i <= n; i++){ cin >> v >> opt; if(opt == 'L') l.push_back(v); else r.push_back(v); } // sort(l.begin(), l.end()); // sort(r.begin(), r.end()); int ans = 0; for(int i = 1; i < l.size(); i++){ ans += abs(l[i] - l[i - 1]); } for(int i = 1; i < r.size(); i++){ ans += abs(r[i] - r[i - 1]); } cout << ans << "\n"; }
C
题意
给定正整数序列, 求有序不可重数对
解答
判断等差数列常见套路:差分相等
然后就没了
代码
#include<bits/stdc++.h> #define int long long using namespace std; const int N = 2e5 + 10, MOD = 998244353; int n; int a[N], cha[N]; signed main(){ // freopen("1.in", "r", stdin); cin >> n; for(int i = 1; i <= n; i++){ cin >> a[i]; cha[i] = a[i] - a[i - 1]; } int lst = cha[2], cnt = 1, ans = n; for(int i = 3; i <= n; i++){ if(cha[i] == lst){ ++cnt; }else{ ans += (1 + cnt) * cnt / 2; cnt = 1; lst = cha[i]; } } if(n != 1) ans += (1 + cnt) * cnt / 2; cout << ans << "\n"; }
D
题意
打怪, 打死每只怪物可以获得一定的经验, 必须按顺序, 但可以跳过一些怪物
对于你打死的第
解答
小 DP
设
每次转移是
odd[i] = max(odd[i - 1], even[i - 1] + a[i]); even[i] = max(even[i - 1], odd[i - 1] + a[i] * 2);
但是边界要稍微处理一下, 我因此 Wa 了一发
代码
#include<bits/stdc++.h> #define int long long using namespace std; const int N = 2e5 + 10, INF = 1e18; int n; int a[N], odd[N], even[N]; signed main(){ // freopen("1.in", "r", stdin); cin >> n; for(int i = 1; i <= n; i++){ cin >> a[i]; } odd[0] = -INF, even[0] = 0; for(int i = 1; i <= n; i++){ odd[i] = max(odd[i - 1], even[i - 1] + a[i]); even[i] = max(even[i - 1], odd[i - 1] + a[i] * 2); } int ans = max(odd[n], even[n]); cout << ans << "\n"; }
E
题意
有一些节点和双向道路。
你的目标是从 1 走到 n, 每次指定
解答
只限定了
不妨枚举经过这些道路的顺序, 共
然后就完事了, 最短路 Floyd 都可以做, 代码复杂度极低哈哈哈
代码
#include<bits/stdc++.h> #define int long long using namespace std; const int N = 4e2 + 10, M = 2e5 + 10, INF = 1e18; int n, m; int u, v, w; int e[N][N], mini; int q, k, b[N]; struct Edge{ int u, v, w; }rec[M]; void Floyd(){ for(int k = 1; k <= n; k++){ for(int i = 1; i <= n; i++){ for(int j = 1; j <= n; j++){ e[i][j] = min(e[i][j], e[i][k] + e[k][j]); } } } } void Dfs(int x, int st, int dis){ if(x == k + 1){ mini = min(mini, dis + e[st][n]); return; } Dfs(x + 1, rec[b[x]].v, dis + e[st][rec[b[x]].u] + rec[b[x]].w); Dfs(x + 1, rec[b[x]].u, dis + e[st][rec[b[x]].v] + rec[b[x]].w); } signed main(){ // freopen("1.in", "r", stdin); cin >> n >> m; for(int i = 1; i <= n; i++){ for(int j = 1; j <= n; j++){ e[i][j] = INF; } e[i][i] = 0; } for(int i = 1; i <= m; i++){ cin >> u >> v >> w; rec[i] = {u, v, w}; e[u][v] = min(e[u][v], w); e[v][u] = e[u][v]; } Floyd(); cin >> q; for(int i = 1; i <= q; i++){ cin >> k; for(int j = 1; j <= k; j++){ cin >> b[j]; } mini = INF; do{ Dfs(1, 1, 0); }while(next_permutation(b + 1, b + 1 + k)); cout << mini << "\n"; } }
F
题意
网格图上行走, 只能往右或往下, 其中一些格点打了标记。
问你从
网格图的大小是
解答
如果格子小一点, 那么就是个普及组题。既然这样, 那么我们的算法肯定是基于标记点个数而非整个网格的。
考虑 ”传统“ 的转移:
所以想起了二维问题的常见处理思路:固定一维扫另一维
从上到下扫过
对于确定的
代码
#include<bits/stdc++.h> #define int long long using namespace std; const int N = 1e7 + 10, INF = 1e18; int h, w, n; int r[N], c[N], ir[N], ic[N], cnt; map <int, int> rec, rcr, rcc; int rr[N], cc[N], cntr, cntc; int pre[N]; vector <int> ans; vector <pair <int, int>> vec[N]; struct Segment_Tree{ struct Node{ int l, r; int maxi, id; }t[N]; void Build(int id, int l, int r){ t[id] = {l, r, 0}; if(l == r) return; int mid = (l + r) / 2; Build(id * 2, l, mid); Build(id * 2 + 1, mid + 1, r); } void Modify(int id, int l, int r, int x, int idx){ if(r < t[id].l || t[id].r < l) return; if(l <= t[id].l && t[id].r <= r){ // t[id].maxi = max(t[id].maxi, x); if(x > t[id].maxi){ t[id].maxi = x; t[id].id = idx; } return; } Modify(id * 2, l, r, x, idx), Modify(id * 2 + 1, l, r, x, idx); if(t[id * 2].maxi > t[id * 2 + 1].maxi){ t[id].maxi = t[id * 2].maxi; t[id].id = t[id * 2].id; }else{ t[id].maxi = t[id * 2 + 1].maxi; t[id].id = t[id * 2 + 1].id; } } pair <int, int> Query(int id, int l, int r){ if(r < t[id].l || t[id].r < l) return {0, 0}; if(l <= t[id].l && t[id].r <= r){ return {t[id].maxi, t[id].id}; } auto a = Query(id * 2, l, r); auto b = Query(id * 2 + 1, l, r); if(b.first > a.first){ a = b; } return a; } }t; signed main(){ // freopen("1.in", "r", stdin); // freopen("1.out", "w", stdout); cin >> h >> w >> n; for(int i = 1; i <= n; i++){ cin >> r[i] >> c[i]; rr[++cntr] = r[i]; cc[++cntc] = c[i]; } sort(rr + 1, rr + 1 + cntr); sort(cc + 1, cc + 1 + cntc); int ca = 0; for(int i = 1; i <= cntr; i++){ if(!rcr[rr[i]]) rcr[rr[i]] = ++ca; } int cb = 0; for(int i = 1; i <= cntc; i++){ if(!rcc[cc[i]]) rcc[cc[i]] = ++cb; } for(int i = 1; i <= n; i++){ vec[rcr[r[i]]].push_back({rcc[c[i]], i}); } t.Build(1, 1, cb); for(int i = 1; i <= ca; i++){ sort(vec[i].begin(), vec[i].end()); for(auto j : vec[i]){ auto v = t.Query(1, 1, j.first); pre[j.second] = v.second; t.Modify(1, j.first, j.first, v.first + 1, j.second); } } // cout << t.Query(1, 1, cb) << "\n" int anss = t.Query(1, 1, cb).first; int ret = t.Query(1, 1, cb).second; r[n + 10] = h, c[n + 10] = w; ans.push_back(n + 10); while(ret){ ans.push_back(ret); ret = pre[ret]; } r[n + 20] = 1, c[n + 20] = 1; ans.push_back(n + 20); reverse(ans.begin(), ans.end()); // for(auto i : ans) cout << i << " "; // cout << "\n"; cout << anss << "\n"; for(int i = 1; i < ans.size(); i++){ int id = ans[i], lst = ans[i - 1]; int x = abs(r[id] - r[lst]), y = abs(c[id] - c[lst]); // cout << x << "+" << y << "\n"; for(int j = 1; j <= x; j++) cout << "D"; for(int j = 1; j <= y; j++) cout << "R"; } cout << "\n"; }
G
题意
俩人在玩游戏, 对象是一棵树, 根为 1
其中一个人 A 在树上指定了
A 想最大化路径长度, B 想最小化, 求对于
解答
披着博弈外皮的贪心
如果告诉你这 这个显然, 或者手算一下就可以得出
如果这
对于 A, 他的策略就是选一个当前情况(有一些边被走过, 不重复计入)下距离 1 最远的点, 加入集合
所以只需要实现一个数据结构, 支持:
- 查询到 1 的最大距离
- 点到根的路径上边权置零
性质: 路径都是向根且连续的!并且每条边只会修改一次
求一下 Dfn 然后线段树维护即可
代码
#include<bits/stdc++.h> #define int long long using namespace std; const int N = 1e6 + 10, MOD = 998244353; int n; int u, v, w; int dfn[N], cdfn, fa[N][2], dis[N], si[N]; // fa : father, edge_val int tag[N]; struct Edge{ int v, w; }; vector <Edge> e[N]; struct Segment_Tree{ struct Node{ int l, r; int maxi, maxid, tag; }t[N]; struct Ret{ int maxi, maxid; }ret; void Update(int id, int x){ t[id].maxi += x; t[id].tag += x; } void Push_down(int id){ Update(id * 2, t[id].tag); Update(id * 2 + 1, t[id].tag); t[id].tag = 0; } void Push_up(int id){ if(t[id * 2].maxi > t[id * 2 + 1].maxi){ t[id].maxi = t[id * 2].maxi; t[id].maxid = t[id * 2].maxid; }else{ t[id].maxi = t[id * 2 + 1].maxi; t[id].maxid = t[id * 2 + 1].maxid; } } void Build(int id, int l, int r){ t[id].l = l, t[id].r = r; if(l == r){ t[id].maxi = dis[l]; t[id].maxid = l; return; } int mid = (l + r) / 2; Build(id * 2, l, mid); Build(id * 2 + 1, mid + 1, r); Push_up(id); } void Modify(int id, int l, int r, int x){ if(r < t[id].l || t[id].r < l){ return; } if(l <= t[id].l && t[id].r <= r){ Update(id, x); return; } Push_down(id); Modify(id * 2, l, r, x); Modify(id * 2 + 1, l, r, x); Push_up(id); } Ret Query(int id, int l, int r){ if(r < t[id].l || t[id].r < l){ return {0, 0}; } if(l <= t[id].l && t[id].r <= r){ return {t[id].maxi, t[id].maxid}; } Push_down(id); Ret a = Query(id * 2, l, r); Ret b = Query(id * 2 + 1, l, r); a = (a.maxi > b.maxi) ? a : b; return a; } }tr; void Get_Dfn(int x, int y, int w){ dfn[x] = ++cdfn; si[dfn[x]] = 1; dis[dfn[x]] = dis[dfn[y]] + w; fa[dfn[x]][0] = dfn[y], fa[dfn[x]][1] = w; for(auto i : e[x]){ if(i.v == y) continue; Get_Dfn(i.v, x, i.w); si[dfn[x]] += si[dfn[i.v]]; } } struct Delete{ int idx, val; }; vector <Delete> del; void Del(int x){ int p = x; while(!tag[p]){ tag[p] = 1; tr.Modify(1, p, p + si[p] - 1, -fa[p][1]); p = fa[p][0]; } } signed main(){ // freopen("1.in", "r", stdin); cin >> n; for(int i = 1; i < n; i++){ cin >> u >> v >> w; e[u].push_back({v, w}); e[v].push_back({u, w}); } Get_Dfn(1, 0, 0); tr.Build(1, 1, n); tag[1] = 1; // 终止点 int ans = 0; for(int i = 1; i <= n; i++){ auto ret = tr.Query(1, 1, n); // cout << ret.maxid << " " << ret.maxi << "\n"; ans += ret.maxi * 2; cout << ans << "\n"; Del(ret.maxid); } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话