Codeforces Round 807 (Div. 2)

1|0基本情况

AB题秒了。

C题搞了半天,搞了一个假的解法,最后还是爆空间了。

D题没想下去。

2|0C. Mark and His Unfinished Essay

Problem - C - Codeforces

2|1错误分析

写出来自己的错解之后没有进一步思考,而是觉得没希望直接做D去了,实则D也没可能半小时写完。

我的错解就是预处理好每个字符串复制后所在的区间,然后查的时候二分找区间,再找字符串。(实则换汤不换药,没有优化空间)。但其实照着这个想法进一步下去,把结构体中的字符串去掉,也就是正解。

using ll = long long; int n, c, q; struct Query { ll l, r; std::string s; Query(ll _l, ll _r, std::string _s) : l(_l), r(_r), s(_s) {} Query() {} void print() { std::cout << "l = " << l << " r = " << r << " s: " << s << std::endl; } void find(int ind) { std::cout << s[ind - l] << std::endl; } }Q[44]; void close_sync() { std::ios::sync_with_stdio(false); std::cin.tie(nullptr); std::cout.tie(nullptr); } void search(int x) { int l = 0, r = c, mid; while(l <= r) { mid = l + r >> 1; if (Q[mid].r < x) { l = mid + 1; } else if (Q[mid].l > x) { r = mid - 1; } else { Q[mid].find(x); return ; } } } void solve() { ll l, r, k; std::string s; std::cin >> n >> c >> q >> s; Q[0] = Query(1, n, s); for (int i = 1; i <= c; i++) { std::cin >> l >> r; std::string temp = ""; for (int j = 0; j < i; j++) { if (Q[j].r < l) continue; if (Q[j].l > r) break; if (Q[j].r >= l && Q[j].l <= l) { int start = l - Q[j].l; if (Q[j].r < r) { int num = Q[j].s.size() - start; temp += Q[j].s.substr(start, num); } else { int num = r - Q[j].l - start + 1; temp += Q[j].s.substr(start, num); } continue; } if (Q[j].r <= r && Q[j].l >= l) { temp += Q[j].s; continue; } if (Q[j].r >= r && Q[j].l <= r) { int start = 0, num = l - Q[j].l + 1; temp += Q[j].s.substr(start, num); continue; } } Q[i] = Query(Q[i - 1].r + 1, Q[i - 1].r + r - l + 1, temp); } for (int i = 1; i <= q; i++) std::cin >> k, search(k); }

2|2正确思路

我们可以发现,任何一个新增的字符,都是由之前的字符复制过来的。

所以,我们可以想个办法,让一个新增的位置回溯到原字符串中的某一个位置。

可以发现,复制的次数非常小,所以我们可以从后往前遍历每次复制操作。

如果我们所求的目标位置在粘贴区间内,就回溯到原区间的对应位置。

不断回溯,就可以找到答案。

#include<iostream> #include<algorithm> #include<map> using ll = long long; int n, c, q; ll l[45], r[45], dif[45], x, len; char a[200010]; void close_sync() { std::ios::sync_with_stdio(false); std::cin.tie(nullptr); std::cout.tie(nullptr); } void solve() { std::cin >> n >> c >> q; for (int i = 1; i <= n; i++) std::cin >> a[i]; len = n;//维护当前长度 for (int i = 1; i <= c; i++) { std::cin >> l[i] >> r[i]; dif[i] = len - l[i] + 1;//每个粘贴的位置和其对应的原位置之间的差值是一定的,我们把这个差值记下来。 len += r[i] - l[i] + 1;//维护当前长度 } for (int i = 1; i <= q; i++) { std::cin >> x; for (int j = c; j; j--) if (x >= l[j] + dif[j] && x <= r[j] + dif[j]) x -= dif[j];//回溯到之前的位置 std::cout << a[x] << std::endl; } } int main() { close_sync(); int _; std::cin >> _; while(_--) solve(); return 0; }

3|0D. Mark and Lightbulbs

Problem - D - Codeforces

我们考虑这个操作究竟能干什么。
我们可以发现,进行一次操作可以 使 01 分界线移动 1,不会改变 01 的段数。
所以如果 st 的 01 段数不同,或者 s1,t1sn,tn 不同,就无解。

可以证明存在一种方案使得移动分界线的时候仅仅往同一方向移动,所以就不会有额外的操作出现。
所以就只需要扫一次得到 s,t 所有的 01 分界线,然后答案就是这些对应两个分界线的差的绝对值的和。

代码是非常简短的

const int N = 2e5 + 10; char s1[N], s2[N]; void solve() { int n; long long ans = 0; std::cin >> n; std::cin >> (s1 + 1) >> (s2 + 1); if (s1[1] != s2[1] || s1[n] != s2[n]) {std::cout<<"-1\n"; return;} std::vector<int> p1, p2; for (int i = 2; i <= n; i++) { if (s1[i] != s1[i - 1]) p1.push_back(i); if (s2[i] != s2[i - 1]) p2.push_back(i); } if (p1.size() != p2.size()) {std::cout<<"-1\n"; return;} for (int i = 0; i < p1.size(); i++) ans += std::abs(p1[i] - p2[i]); std::cout << ans << '\n'; }

__EOF__

本文作者Kdlyh
本文链接https://www.cnblogs.com/kdlyh/p/17896633.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   加固文明幻景  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示