Codeforces Round #830 (Div. 2) A-D
A
题解
知识点:贪心,数论。
先求出序列最大公约数 ,如果为 直接输出 。
否则,尝试用最后一个数操作, 则可以,花费为 。
否则,用倒数第二个数操作, (不必担心 ,因为此时上一步一定成功),花费为 。
否则,用倒数两个数操作,一定成功,因为 ,花费为 。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> #define ll long long using namespace std; int a[27]; bool solve() { int n; cin >> n; for (int i = 1;i <= n;i++) cin >> a[i]; int d = a[1]; for (int i = 2;i <= n;i++) d = __gcd(a[i], d); if (d == 1) cout << 0 << '\n'; else if (__gcd(d, n) == 1) cout << 1 << '\n'; else if (__gcd(d, n - 1) == 1) cout << 2 << '\n'; else cout << 3 << '\n'; return true; } int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int t = 1; cin >> t; while (t--) { if (!solve()) cout << -1 << '\n'; } return 0; }
B
题解
知识点:贪心。
显然左侧已经排好的不用管,从第一段 开始记录后面一共分成的段数 (如 0000|111|00|1|0|1|0
有 段)。若 ,则从第一段开始使用操作,每次可以将一段变为 (后面也会改变),直到最后一段不用更改,一共操作 次。另外,如果 ,答案也是 。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> #define ll long long using namespace std; bool solve() { int n; cin >> n; string s; cin >> s; s = "?" + s; bool st = 0; int cnt = 0; for (int i = 1;i <= n;i++) { if (s[i] != st + '0') { cnt++; st ^= 1; } } cout << max(cnt - 1, 0) << '\n'; return true; } int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int t = 1; cin >> t; while (t--) { if (!solve()) cout << -1 << '\n'; } return 0; }
C
题解
知识点:枚举,双指针,位运算,前缀和,贪心。
因为异或相当于不进位的加法,那么前缀和减去前缀异或和一定是非减的,于是一个贪心的结论:最大值一定是 的值。接下来需要用这个值,求一个最小区间。
为了方便区间运算,我们预处理一个前缀和,以及一个前缀异或和。
可以证明,最多删除 个非 元素,否则区间值必然减小。因为在 int
范围内, 个二进制位,最多能分配给 个数一个不同为值的 ,这样能使这 个数对答案没有贡献,实际情况不会超过 ,因此我们放心大胆的枚举端点就行了。我们只需要考虑非 点,遍历一遍存一下非 点坐标即可。左端点从左往右,内循环右端点从右往左,区间等于最大值的如果长度更小就更新答案。
另外,需要特判没有非 元素的情况,此时直接输出 即可。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> #define ll long long using namespace std; int a[100007]; ll sum[100007], sumx[100007]; bool solve() { int n, q; cin >> n >> q; for (int i = 1;i <= n;i++) cin >> a[i]; vector<int> pos; for (int i = 1;i <= n;i++) { sum[i] = sum[i - 1] + a[i]; sumx[i] = sumx[i - 1] ^ a[i]; if (a[i]) pos.push_back(i); } while (q--) { int L, R; cin >> L >> R; int l = lower_bound(pos.begin(), pos.end(), L) - pos.begin(); int r = upper_bound(pos.begin(), pos.end(), R) - pos.begin() - 1; auto get = [&](int _l, int _r) {return sum[_r] - sum[_l - 1] - (sumx[_r] ^ sumx[_l - 1]);}; ll val = get(L, R); int ansl = 0, ansr = n + 1; for (int i = l;i <= r;i++) { if (get(pos[i], pos[r]) < val) break;//可以证明无论左右端点至多删除31个非0元素对答案没有影响(0,2,4,8,...鸽巢原理) for (int j = r;j >= i;j--) { if (get(pos[i], pos[j]) < val) break; if (pos[j] - pos[i] + 1 < ansr - ansl + 1) { ansl = pos[i]; ansr = pos[j]; } } } if (ansr - ansl + 1 > n) cout << L << ' ' << L << '\n'; else cout << ansl << ' ' << ansr << '\n'; } return true; } int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int t = 1; cin >> t; while (t--) { if (!solve()) cout << -1 << '\n'; } return 0; }
D1
题解
知识点:STL,枚举。
用一个 set<ll> vis
维护出现过的元素,再用一个 map<ll,ll> last
维护某个元素上一次的询问结果,下一次询问这个元素时直接从上一次结果开始。
每个元素 只经过其倍数一次,共 次。所有元素次数之和 。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> #define ll long long using namespace std; int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int q; cin >> q; set<ll> vis; map<ll, ll> last; while (q--) { char op; ll x; cin >> op >> x; if (op == '+') { vis.insert(x); } else { if (!last.count(x)) last[x] = x; while (vis.count(last[x])) last[x] += x; cout << last[x] << '\n'; } } return 0; }
D2
题解
知识点:STL,枚举。
因为存在删除已存在元素的操作,这意味着我们之前得到的答案在未来可能不适用。
因此需要对每个已询问过的数字维护一个已删除集合 map<ll,set<ll>> del
,来得到目前某个元素的最大询问结果内,是否有在序列中被删除的倍数,这些倍数是可以替换最大询问结果,重新作为 mex
的。
对于已删除集合的维护,需要对每个询问过程中遍历到的,且存在于序列中的倍数,维护一个影响列表 map<ll,vector<ll>> chg
,来得到修改序列某个元素的状态时,哪些询问过的元素的已删除集合会被修改。
如此我们就维护了带删除求 mex
的数据结构。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> #define ll long long using namespace std; int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int q; cin >> q; set<ll> vis;//序列中存在的元素 map<ll, ll> last;//某元素最大询问结果:记录需要维护的数据上界,超出最大询问结果的不需要考虑 map<ll, set<ll>> del;//某元素的已删除集合:最大询问结果内,目前不存在于序列中的倍数 map<ll, vector<ll>> chg;//某元素的影响列表:更改序列中某元素状态,已删除集合将发生变化的元素列表 while (q--) { char op; ll x; cin >> op >> x; if (op == '+') { vis.insert(x); for (auto y : chg[x]) del[y].erase(x);//删除受x影响元素的已删除集合中的x,因为x已存在 } else if (op == '-') { vis.erase(x); for (auto y : chg[x]) del[y].insert(x);//增加受x影响元素的已删除集合中的x,因为x已删除 } else { if (!last.count(x)) last[x] = x;//新增已询问元素 if (del[x].size()) cout << *del[x].begin() << '\n';//已删除集合存在元素,优先使用 else {//考虑最大询问结果是否可行,否则扩大最大询问结果 while (vis.count(last[x])) { chg[last[x]].push_back(x);//已存在的x的倍数,在未来可能会被修改状态,影响x的结果 last[x] += x; } cout << last[x] << '\n'; } } } return 0; }
本文来自博客园,作者:空白菌,转载请注明原文链接:https://www.cnblogs.com/BlankYang/p/16829557.html
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
· .NET 10 首个预览版发布,跨平台开发与性能全面提升
· 《HelloGitHub》第 107 期
· 全程使用 AI 从 0 到 1 写了个小工具
· 从文本到图像:SSE 如何助力 AI 内容实时呈现?(Typescript篇)