题解:P16375 [IATI 2026] Evilution
lailai0916 · · 题解
题意简述
给定一段初始 DNA。
四种字符各有一个替换串。 每天会同时替换当前 DNA 中的所有字符。
每次询问给定
解题思路
区间计数可以由两个前缀计数相减得到。
因此,只需解决前
先考虑一个字符展开若干天后的整体信息。
令
第零天的展开只有字符本身。
若
查询位置不超过
还需快速求一个替换串前若干个字符的贡献。 对初始串和四个替换串, 分别预处理四种字符的前缀出现次数。
假设当前处理字符串
各块长度均为正数。
因此,完整块的总长度随
取走这些完整块后,只有两种情况:
- 所需前缀已经取完,直接结束
- 终点落入下一个字符的展开块
设下一个字符为
代码用循环执行这个过程。 每次循环都会减少展开天数。 这样无需递归,也只会追踪唯一的不完整块。
下面证明该过程得到正确的前缀计数。
一次替换后,当前字符串会变成若干展开块的拼接。 二分找到的最长前缀恰好包含所有完整块。 算法一次性加入这些块的完整信息。 若仍有剩余,只进入紧随其后的唯一一个块。
所以,每个被计入的位置恰好处理一次。 未进入目标前缀的位置不会被处理。 循环结束时,所得信息就是目标前缀的信息。
最后处理很大的
当
预处理首字符转移的倍增表。
先跳过
预处理需要
参考代码
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
__extension__ using i128=__int128;
const int N=100005;
const ll inf=4000000000000000000LL;
struct Data
{
ll len,c[4];
};
string s[5];
int pre[5][4][N],go[61][4];
Data f[61][4];
int id(char c)
{
if(c=='A')return 0;
if(c=='C')return 1;
if(c=='G')return 2;
return 3;
}
ll cap(i128 x)
{
return x>inf?inf:(ll)x;
}
void add(Data &x,const Data &y,int w)
{
x.len=cap((i128)x.len+(i128)y.len*w);
for(int i=0;i<4;i++)x.c[i]=cap((i128)x.c[i]+(i128)y.c[i]*w);
}
ll get_len(int z,int p,int k)
{
i128 res=0;
for(int i=0;i<4;i++)res+=(i128)pre[z][i][p]*f[k][i].len;
return cap(res);
}
Data get(int z,int p,int k)
{
Data res={};
for(int i=0;i<4;i++)add(res,f[k][i],pre[z][i][p]);
return res;
}
Data walk(int z,int k,ll x)
{
Data res={};
while(x)
{
int l=0,r=s[z].size();
while(l<r)
{
int mid=(l+r+1)/2;
if(get_len(z,mid,k)<=x)l=mid;
else r=mid-1;
}
Data t=get(z,l,k);
add(res,t,1);
x-=t.len;
if(!x)break;
int c=id(s[z][l]);
if(!k)
{
res.len++;
res.c[c]++;
break;
}
z=c+1;
k--;
}
return res;
}
int jump(int c,ll k)
{
for(int i=0;k;i++,k>>=1)if(k&1)c=go[i][c];
return c;
}
Data prefix(ll k,ll x)
{
if(!x)return {};
if(k<=60)return walk(0,k,x);
int c=jump(id(s[0][0]),k-60);
return walk(c+1,59,x);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
for(int i=0;i<5;i++)cin>>s[i];
for(int i=0;i<5;i++)
{
int n=s[i].size();
for(int j=1;j<=n;j++)
{
for(int k=0;k<4;k++)pre[i][k][j]=pre[i][k][j-1];
pre[i][id(s[i][j-1])][j]++;
}
}
for(int i=0;i<4;i++)
{
f[0][i].len=1;
f[0][i].c[i]=1;
go[0][i]=id(s[i+1][0]);
}
for(int k=1;k<=60;k++)
{
for(int i=0;i<4;i++)
{
int n=s[i+1].size();
for(int j=0;j<4;j++)add(f[k][i],f[k-1][j],pre[i+1][j][n]);
go[k][i]=go[k-1][go[k-1][i]];
}
}
int q;
cin>>q;
while(q--)
{
ll k,l,r;
cin>>k>>l>>r;
Data x=prefix(k,r+1),y=prefix(k,l);
cout<<x.c[0]-y.c[0]<<' '<<x.c[1]-y.c[1]<<' '<<x.c[2]-y.c[2]<<' '<<x.c[3]-y.c[3]<<'\n';
}
return 0;
}