P9016 题解

P9016 题解

题面

原题传送门

思路

发现如果暴力去做的话每一次修改长度就会增长许多,直接维护是不行的,那就要思考如何维护能让空间在 \(O(\sum|S|)\) 左右。

考虑维护 \(26\) 个 DAG,每一个 DAG 表示每一个字母会变成什么,且在每个 DAG 中的每个点出度只有 \(2\)

我们维护每一个 DAG 的起点,从后往前做,对于一个字符 \(c\) 要变成字符串 \(s\)(以下为 \(s\) 编号为 \(0,1,\cdots,|s|-1\)),有两种情况。

  1. \(|s|=1\),那么直接让 \(c\) 的起点变成 \(s_0\) 就好了。
  2. \(|s|\neq1\),那么我们从 \(0,1,\cdots,|s|-1\) 开始两两合并,第 \(i\) 次合并,先建一个虚点,连向上一个虚点(当第一次合并时则是 \(s_0\) 的起点)和 \(s_i\) 的起点,最后把 \(c\) 的起点更新为最后一个虚点。

这样其实就相当于多次重复调用初始指向自己的节点,可以利用 b 化成 bbb 来理解。

而从后往前做能保证每一次合并的时候,每个字母起点往下的 DAG 都是已经统计好的。

现在开始考虑输出,输出的就是以 a 为起点往下输出 \([l,r]\) 的区间,这其实类似平衡树,我们可以统计每一个点左边和右边连向的 DAG 的大小,这样假设我们要输出以 \(p\) 为根的子树中的 \([l,r]\),有两种情况。

  1. 左边的 DAG 的大小大于等于 \(l\),那么说明区间有一部分在左边的 DAG 中。
  2. 左边的 DAG 的大小小于 \(r\),那么说明有一部分在右边的 DAG 中。

递归下去即可。

这边要注意一个细节,因为字符串总长度增长过快,左右 DAG 的大小可能会爆 long long,所以可以设置一个极大值和大小取 min,这样问题就解决了。

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
// #pragma GCC optimize(2)
// #pragma GCC optimize(3)
// #define gc getchar
#define gc()(p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
#define FILE(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout);
#define FIN(s) freopen(s".in","r",stdin);
#define FOUT(s) freopen(s".out","w",stdout);
#define ll long long
#define re register int
#define rl register ll
#define il inline
using namespace std;
const ll MN=2e5+5,INF=2e18;
ll l,r,n,rt[30],idx;
string s[MN];
char buf[1<<23],*p1=buf,*p2=buf,c[MN];
struct dag{char ch;ll ls,rs,sz;}t[MN<<1];
il void write(rl n){if(n<0){putchar('-');write(-n);return;}if(n>9)write(n/10);putchar(n%10+'0');}
il ll read(){ll x=0,f=1;char ch=gc();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}return x*f;}
il void print(ll p, ll l, ll r){
    if(t[p].ch!='~'){putchar(t[p].ch);return;}
    if(t[t[p].ls].sz>=l) print(t[p].ls,l,min(r,t[t[p].ls].sz));
    if(t[t[p].ls].sz<r) print(t[p].rs,max(l-t[t[p].ls].sz,1ll),r-t[t[p].ls].sz);
}
int main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    cin>>l>>r>>n;
    for(re i=1; i<=n; ++i) cin>>c[i]>>s[i];
    for(re i=0; i<26; ++i) t[rt[i]=++idx]={i+'a',0,0,1};
    for(re i=n; i; --i){
        ll p=rt[s[i][0]-'a'];
        for(re j=1; j<s[i].size(); ++j){
            ll x=s[i][j]-'a';
            t[++idx]={'~',p,rt[x],min(INF,t[p].sz+t[rt[x]].sz)};
            p=idx;
        }
        rt[c[i]-'a']=p;
    }print(rt[0],l,r);
    return 0;
}//250815
posted @ 2025-08-25 10:35  naroto2022  阅读(7)  评论(0)    收藏  举报