题解:CF217E Alien DNA
题目链接:洛谷 Codeforces
分享一个比较不需要脑子的做法。
思路
因为只需要输出前 \(k\) 个字符,所以很多操作都是无意义的。考虑如何判断哪些操作才是有意义的,并不关系复制的串具体是什么,只关心它在最终序列中的位置,因此可以只维护每一段区间对应的是哪一个操作,第 \(i\) 个操作的区间为 \([l,r]\) 就相当于在当前第 \(r\) 个位置后面插入个长度为 \(r-l+1\),颜色为 \(i\) 的段。因为 \(n\leq 5000\),所以可以暴力维护颜色段,最初只有长度为初始串长的一段,每插入一段就把原来在插入位置的段分成两段,再把新的段放在中间。一次操作最多只会增加两段,总段数是 \(n\) 级别的,用 vector 或链表暴力操作的复杂度是 \(O(n^2)\) 的,已经可以接受,不过应该也可以用平衡树什么的做到 \(O(n\log n)\)。
这样就得到了每个操作最终的位置。取前 \(k\) 个位置,则只用考虑导致这些位置出现的操作。对应到每个操作,设前 \(k\) 项有 \(c\) 个位置来自这个操作,则这个操作就只需要插入扰乱处理后的串的前 \(c\) 个元素。这个扰乱处理是平凡的,可以直接截取一段,暴力处理后再插入,问题就变成了维护一个字符串,支持提取一个子串以及在某个位置后插入一个串。
这个问题就是块状链表的板子了。不过没必要真的去写一个又臭又长的块状链表,可以用 \(\text {__gnu_cxx}\) 里的 \(\text {rope}\) 实现。
如果你不会 \(\text {rope}\),下面列出了本题可能要用到的它的操作:
操作
- 引用、声明
#include <ext/rope>
#include<bits/extc++.h>//可以不用上面那个,直接把 std 换成 ext
using namespace __gnu_cxx;//引用命名空间
rope<char> c1;//定义一个 char 类型的 rope
crope c2;//这个和上面是等价的
- string 转 rope
string s="114514";
crope c=s.c_str();
- 插入子串
crope c="abbba";
string s="cac";
c.insert(3,s.c_str());//从 3 位置插入 s
cout<<c;//输出为 "abbcacba"
- 截取子串
crope c1="1919810";
crope c2=c1.substr(2,3);//从 c1 的下标 2 开始截取 3 个字符
cout<<c2;//输出为 "198"
然后还有迭代器遍历的操作,和其他容器是类似的,这里就不列举了。
最后分析一下复杂度:\(\text {rope}\) 的截取子串和插入子串的复杂度都是 \(\log N+|S|\),其中 \(N\) 是 \(\text {rope}\) 大小,\(|S|\) 是操作的串长。因为只有 \(n\) 次操作,总长不超过 \(k\),所以这部分的总时间复杂度为 \(O(n\log k+k)\)。需要注意的是,查找连续位置时要使用迭代器,而不能逐个取值,否则复杂度会变成 \(k\log k\)(不过似乎还是跑得很快)。
代码
#include <bits/extc++.h>
using namespace std;
using namespace __gnu_cxx;
#define pii pair<int,int>
#define ll long long
const int M=5e3+5;
int n,k,m,sz[M];
pii q[M];
vector<pii > a;
string s;
signed main()
{
cin>>s;
n=s.size();
scanf("%d%d",&k,&m);
a.push_back({n,0});
for(int i=1;i<=m;i++)
{
scanf("%d%d",&q[i].first,&q[i].second);
int d=q[i].second-q[i].first+1;
int res=q[i].second;
for(auto it=a.begin();it!=a.end();it++)
{
if(it->first>=res)
{
pii v=*it;
it->first=res;
v.first-=res;
it++;
it=a.insert(it,{d,i});
if(v.first)
a.insert(next(it),v);
break;
}
res-=it->first;
}
}
int res=k;
for(auto it=a.begin();it!=a.end();it++)
{
sz[it->second]+=min(res,it->first);
if(it->first>=res)
break;
res-=it->first;
}
crope c=s.c_str();
crope cc=c.substr(0);
for(int i=1;i<=m;i++)
if(sz[i])
{
int d=q[i].second-q[i].first+1;
int d1=min(sz[i],d/2);
int d2=sz[i]-d1;
string add;
crope nw=c.substr(q[i].first-1,max(d1,d2)*2);
auto p=nw.begin();
for(auto it=next(p);d1--;it+=2)
add+=*it;
for(auto it=p;d2--;it+=2)
add+=*it;
c.insert(q[i].second,add.c_str());
}
cout<<c.substr(0,k);
return 0;
}

浙公网安备 33010602011771号