基本情况
AB题秒了。
C题搞了半天,搞了一个假的解法,最后还是爆空间了。
D题没想下去。
C. Mark and His Unfinished Essay
错误分析
写出来自己的错解之后没有进一步思考,而是觉得没希望直接做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);
}
正确思路
我们可以发现,任何一个新增的字符,都是由之前的字符复制过来的。
所以,我们可以想个办法,让一个新增的位置回溯到原字符串中的某一个位置。
可以发现,复制的次数非常小,所以我们可以从后往前遍历每次复制操作。
如果我们所求的目标位置在粘贴区间内,就回溯到原区间的对应位置。
不断回溯,就可以找到答案。
#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;
}
D. Mark and Lightbulbs
我们考虑这个操作究竟能干什么。
我们可以发现,进行一次操作可以 使 01 分界线移动 \(1\),不会改变 01 的段数。
所以如果 \(s\) 和 \(t\) 的 01 段数不同,或者 \(s_1,t_1\)、\(s_n,t_n\) 不同,就无解。
可以证明存在一种方案使得移动分界线的时候仅仅往同一方向移动,所以就不会有额外的操作出现。
所以就只需要扫一次得到 \(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';
}
标签:std,int,ll,Codeforces,long,cin,Div,807,void
From: https://www.cnblogs.com/kdlyh/p/17896633.html