Codeforces Round #823 (Div. 2) A-D

🚀 优质资源分享 🚀

学习路线指引(点击解锁)知识定位人群定位
🧡 Python实战微信订餐小程序 🧡进阶级本课程是python flask+微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一个全栈订餐系统。
💛Python量化交易实战💛入门级手把手带你打造一个易扩展、更安全、效率更高的量化交易系统

比赛链接

A

题解

知识点:贪心。

对于一个轨道,要么一次性清理,要么一个一个清理。显然,如果行星个数大于直接清理的花费,那么选择直接清理,否则一个一个清理。即 ∑min(c,cnt[i])∑min(c,cnt[i])\sum \min (c,cnt[i]),其中 cnt[i]cnt[i]cnt[i] 表示轨道 iii 的行星个数。

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

空间复杂度 O(1)O(1)O(1)

代码



|  | #include  |
|  | #define ll long long |
|  |  |
|  | using namespace std; |
|  |  |
|  | int cnt[107]; |
|  |  |
|  | bool solve() { |
|  | int n, c; |
|  | cin >> n >> c; |
|  | memset(cnt, 0, sizeof(cnt)); |
|  | for (int i = 1;i <= n;i++) { |
|  | int x; |
|  | cin >> x; |
|  |  cnt[x]++; |
|  |  } |
|  | int ans = 0; |
|  | for (int i = 1;i <= 100;i++) ans += min(c, cnt[i]); |
|  | cout << ans << '\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

题解

方法一

知识点:三分。

按位置从小到大排列,显然约会花费是一个关于 x0x0x_0 的单谷函数,因此可以三分位置。

由于位置最大有 10810810^8 ,但点的个数只有 10510510^5 ,考虑先用 map 存储有序对 (x,t)(x,t)(x,t) ,其中 ttt 是位置 xxx 的人最大打扮时间,因为比这个时间少的一定不影响结果。遍历结束以后把 map 内容移到 vector 中用 pair 存储用以三分,check 函数则只要遍历一遍 vector 即可。

时间复杂度 O(nlogmax(eps))O(nlog⁡max(eps))O(n \log \max(eps))

空间复杂度 O(n)O(n)O(n)

方法二

知识点:贪心。

把 ttt 等效进位置,如果 xixix_i 在 x0x0x_0 左侧,则等效位置是 xi−txi−txi - t ;如果 xixix_i 在 x0x0x_0 右侧,则等效位置是 xi+txi+tx_i + t 。

所有点的左侧等效位置最左的位置,就是等效区间左端点;所有点的右侧等效位置最右的位置就是等效区间的右端点。

如果等效区间的左右端点来自于不同两点的等效点,那么等效区间的中点一定在这两点之间,否则原来的点必有一个能覆盖另一个点,等效区间的左右端点就属于同一个点的等效点。

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

空间复杂度 O(n)O(n)O(n)

代码

方法一



|  | #include  |
|  | #define ll long long |
|  |  |
|  | using namespace std; |
|  |  |
|  | int x[100007]; |
|  | map<int, int> mp; |
|  | vector<pair<int, int>> v; |
|  |  |
|  | double check(double mid) { |
|  | double mx = 0; |
|  | for (auto [i, j] : v) { |
|  |  mx = max(mx, abs(i - mid) + j); |
|  |  } |
|  | return mx; |
|  | } |
|  |  |
|  | bool solve() { |
|  |  mp.clear(); |
|  |  v.clear(); |
|  | int n; |
|  | cin >> n; |
|  | for (int i = 1;i <= n;i++) { |
|  | cin >> x[i]; |
|  |  mp[x[i]] = 0; |
|  |  } |
|  | for (int i = 1;i <= n;i++) { |
|  | int T; |
|  | cin >> T; |
|  |  mp[x[i]] = max(mp[x[i]], T); |
|  |  } |
|  | for (auto [i, j] : mp) { |
|  |  v.push\_back({ i,j }); |
|  |  } |
|  |  |
|  | double l = 0, r = v.back().first; |
|  | while (abs(r - l) >= 1e-7) { |
|  | double mid1 = l + (r - l) / 3; |
|  | double mid2 = r - (r - l) / 3; |
|  | if (check(mid1) <= check(mid2)) r = mid2; |
|  | else l = mid1; |
|  |  } |
|  | cout << fixed << setprecision(10) << l << '\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; |
|  | } |


方法二



|  | #include  |
|  | #define ll long long |
|  |  |
|  | using namespace std; |
|  |  |
|  | int x[100007], T[100007]; |
|  |  |
|  | bool solve() { |
|  | int n; |
|  | cin >> n; |
|  | int l = 1e9, r = 0; |
|  | for (int i = 1;i <= n;i++) cin >> x[i]; |
|  | for (int i = 1;i <= n;i++) cin >> T[i]; |
|  | for (int i = 1;i <= n;i++) { |
|  |  l = min(x[i] - T[i], l);///最左侧等效点 |
|  |  r = max(x[i] + T[i], r);///最右侧等效点 |
|  |  } |
|  | cout << fixed << setprecision(8) << (l + r) / 2.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

题解

知识点:贪心。

因为要字典序最小,那么一个数字他后面没有更小的数字则可以保留,其他都应该删除,所以从右往左找一个合法的保留序列,其他的数字加一,并且都是位置随意的,于是可以插入到保留下来的序列,并使插入后的序列是从小到大字典序最小的排列。因此直接把保留序列外的数字加一以后,对整个序列排序即可。

也可以直接桶排序。

时间复杂度 O(nlogn)O(nlog⁡n)O(n \log n)

空间复杂度 O(n)O(n)O(n)

代码



|  | #include  |
|  | #define ll long long |
|  |  |
|  | using namespace std; |
|  |  |
|  |  |
|  | bool solve() { |
|  | string s; |
|  | cin >> s; |
|  | int mi = 10; |
|  | for (int i = s.size() - 1;i >= 0;i--) { |
|  | if (s[i] - '0' <= mi) mi = s[i] - '0'; |
|  | else s[i] = min(s[i] + 1, '9' + 0); |
|  |  } |
|  |  sort(s.begin(), s.end()); |
|  | cout << s << '\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; |
|  | } |


D

题解

知识点:构造。

注意到操作不会改变无序对 (ai,bn−i+1)(ai,bn−i+1)(a_i, b_{ n - i + 1 }) 数量以及种类。

引理:a=ba=ba = b ,当且仅当无序对是回文的

充分性:

当 a=ba=ba = b 时,如果 iii 处存在一组无序对 (x,y)(x,y)(x, y) ,则必然会在 n−i+1n−i+1n-i+1 产生相同一组无序对 (y,x)(y,x)(y, x) ,除非当 nnn 为奇数时,可以在中间产生一个元素相同的无序对 (x,x)(x,x)(x,x) ,因此 a=ba=ba = b 时,无序对必然成回文状。

必要性:

当无序对是回文的,则第 iii 组无序对 (x,y)(x,y)(x,y) 可以对应第 n−i+1n−i+1n-i+1 组无序对 (y,x)(y,x)(y,x) ,即 ai=biai=bia_i = b_i ,所以 a=ba=ba = b 。

充要条件:YES 当且仅当无序对 (ai,bn−i+1)(ai,bn−i+1)(a_i, b_{ n - i + 1 }) 中元素不同的无序对有偶数个,元素相同的无序对仅在 nnn 为奇数时至多 111 种有奇数个。

充分性:

根据引理,显然满足右边条件。

必要性:

显然没有任何限制时,给出的无序对条件能排列成回文的,现在尝试证明其必然可构造无序对回文。

注意到操作 k=ik=ik = i 可以使得 a[1⋯k]a[1⋯k]a[1 \cdots k] 和 b[k⋯n]b[k⋯n]b[k\cdots n] 交换位置,即 (a[k],b[n−k+1])(a[k],b[n−k+1])(a[k], b[n - k + 1]) 这一组无序对被置换到了 111 号位置,同时 (a[1],b[n])(a[1],b[n])(a[1],b[n]) 这一组无序对被置换到了 iii 号位置,但这不会改变 a[k+1⋯n]a[k+1⋯n]a[k+1 \cdots n] 和 b[1⋯k−1]b[1⋯k−1]b[1\cdots k-1] 的顺序,即第 k+1k+1k+1 到 nnn 组无序对及其实际元素顺序没有改变。因此,如果我们想要将无序对通过操作变成一个我们想要的顺序,可以从右往左构造。

假设 i+1i+1i+1 到 nnn 的无序对都安排好了,现在 iii 号位置想要 j(j≤i)j(j≤i)j (j\leq i) 号位置的无序对时,可以先 k=jk=jk=j ,将 jjj 号替换到 111 号,然后 k=ik=ik=i ,将 111 号替换 iii 号,过程中 i+1⋯ni+1⋯ni+1 \cdots n 的无序对不会改变,包括实际元素顺序。

上述操作最后结果是无序对 jjj 替换到 iii ,且 jjj 号无序对元素的实际顺序不会改变。但如果我们希望实际元素的顺序也发生改变,我们可以加一个步骤 k=1k=1k = 1 在中间,即通过 k=j,k=1,k=ik=j,k=1,k=ik = j, k = 1, k = i 替换 iii 号后的 jjj 号元素实际顺序与原来是相反的,这也是为什么我们只需要知道无序对顺序即可,因为元素实际顺序是可以随时改变的。

通过上述操作我们可以实现无序对的任意排列,以及无序对实际元素的顺序。因此无序对满足回文条件时,必然可以构造出无序对回文。于是根据引理,得到 a=ba=ba = b 。

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

空间复杂度 O(n)O(n)O(n)

代码



|  | #include  |
|  | #define ll long long |
|  |  |
|  | using namespace std; |
|  |  |
|  | string a, b; |
|  | int cnt[26][26]; |
|  |  |
|  | bool solve() { |
|  | memset(cnt, 0, sizeof(cnt)); |
|  | int n; |
|  | cin >> n; |
|  | string a, b; |
|  | cin >> a >> b; |
|  | for (int i = 0;i < n;i++) { |
|  | int x = a[i] - 'a', y = b[n - 1 - i] - 'a'; |
|  | if (x > y) swap(x, y); |
|  |  cnt[x][y]++; |
|  |  } |
|  |  |
|  | bool ok = true; |
|  | int esum = 0; |
|  | for (int i = 0;i < 26;i++) { |
|  | for (int j = i;j < 26;j++) { |
|  | if (i == j) esum += cnt[i][j] & 1; |
|  | else ok &= !(cnt[i][j] & 1); |
|  |  } |
|  |  } |
|  |  |
|  | if (ok && esum <= (n & 1)) cout << "YES" << '\n'; |
|  | else cout << "NO" << '\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; |
|  | } |


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值