CF1687C Sanae and Giant Robot 题解
题目链接:https://codeforces.com/contest/1687/problem/C
题意简述
有两个长为
- 选择一条线段
,若 ,将 的 段置为 。
问是否能使
题解
这题其实非常简单。首先要转化问题。令两数组差的前缀和
- 若
,将 的 段置为 。
问题等价于能否使
这时问题已经很清晰了,我们倒序考虑操作,不难得到如下结论:
-
一次
的操作是毫无意义的,也就是操作总有 。 -
那么,既然所有操作都要端点为
时才能操作,且最终的目标也是全 ,只要一次操作可以进行,立即进行总是最优的。且已经执行该操作后,不会需要再次执行。
考虑如何模拟这个过程。我们很容易想到类似拓扑排序的模型,将每条线段置为
每个点只会被置零一次。可以使用 set
维护尚未被置零的点,或者使用并查集甚至链表等其他手段保证正确复杂度。时间复杂度
代码实现
我练习写了一版并查集,但再想想用 set
会方便简洁许多,因此在这里附上更短的代码。
#define int i64 void solve() { int n, m; cin >> n >> m; vector<int> a(n + 1), b(n + 1); for (int i = 1; i <= n; i++) cin >> a[i]; for (int i = 1; i <= n; i++) cin >> b[i]; vector<int> s(n + 1); for (int i = 1; i <= n; i++) { s[i] = s[i - 1] + a[i] - b[i]; } vector<pair<int, int>> op; queue<int> q; vector<vector<int>> adj(n + 1); set<int> nz; for (int i = 1; i <= n; i++) { if (s[i]) nz.insert(i); } for (int i = 0; i < m; i++) { int l, r; cin >> l >> r; l--; op.push_back({l, r}); if (!s[l] && !s[r]) { q.push(i); } if (s[l]) adj[l].push_back(i); if (s[r]) adj[r].push_back(i); } while (q.size()) { int id = q.front(); q.pop(); auto [l, r] = op[id]; for (auto it = nz.lower_bound(l); it != nz.end() && *it <= r; it = nz.erase(it)) { int x = *it; s[x] = 0; for (int u : adj[x]) { auto [l, r] = op[u]; if (!s[l] && !s[r]) q.push(u); } } } if (ranges::count(s, 0) != (i64)s.size()) cout << "No\n"; else cout << "Yes\n"; } #undef int
分类:
做题记录
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具