题解: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;
}
posted @ 2026-09-11 11:07  ShanLing3  阅读(6)  评论(0)    收藏  举报
//雪花飘落效果