[HNOI2019] JOJO
作者:互联网
一、题目
二、解法
首先不考虑 \(2\) 操作,考虑怎么向串的末尾加入 \(x\) 个字符 \(c\),下文将其称之为“一段”。
注意到关键条件:对于加入的字符 \(c\),保证之前串尾的字符不是 \(c\),考虑整段整段地跑 \(\tt kmp\),在两个段完全相同(指个数和字符)时跳出。根据题目条件,如果两个段部分匹配,后面再加字符的话就会匹配不上,所以段不完全相同的时候还要暴力跳 \(nxt\) 指针。
考虑在跳跃的时候计算新增 \(\tt border\) 的总和,假设末尾的段是 \((x,c)\),匹配到前面的段是 \((y,c)\):
- \(x=y\),对应的 \(\tt border\) 是公差为 \(1\) 的等差序列,此时找到匹配可以直接跳出。
- \(x<y\),会直接失配,但是对应的 \(\tt border\) 还是公差为 \(1\) 的等差序列。
- \(x>y\),可以部分地确定 \(y\) 个末尾段的 \(\tt border\),仍然构成等差序列,确定剩下的部分我们需要跳 \(nxt\) 指针。
这个过程需要记录最后一段确定到的位置 \(cur\),但是注意跳到第一段时,匹配条件放宽成 \(x\geq y\),未确定的 \(\tt border\) 长度都应该是 \(y\),需要特判。时间复杂度和传统的 \(\tt kmp\) 是一致的,都是 \(O(n)\)
现在考虑怎么可持久化,把操作离线下来,发现会构成一棵操作树,每个时刻的串对应树上的一个状态,树上的边对应新添加的段。那么在操作树上处理询问,就只需要支持可撤销 \(\tt kmp\) 即可。
由于 \(\tt kmp\) 不会改变前面的信息,我们想限制单次插入在 \(O(\log n)\) 内的时间完成。用到的关键结论是:若最长 \(\tt border\) 长度 \(L>n/2\),所有长度大于 \(n/2\) 的 \(\tt border\) 构成一个公差为 \(n-L\) 的等差数列。
并且这些等差数列对应的匹配点都是一致的,所以在跳 \(nxt\) 指针至少一次之后,可以直接跳过一个等差数列。那么长度每次至少会减少一半,所以单次插入的时间复杂度 \(O(\log n)\)
时间复杂度 \(O(n\log n)\)
#include <cstdio>
#include <vector>
#include <iostream>
using namespace std;
const int M = 100005;
const int MOD = 998244353;
#define int long long
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m,id[M],f[M],ans[M],len[M];vector<int> g[M];
struct node{int x,c;}s[M],a[M];
int C(int x) {return x*(x-1)/2;}
void dfs(int u,int n)
{
if(n==-1)
{
for(int v:g[u]) dfs(v,n+1);
return ;
}
s[n]=a[u];len[n+1]=len[n]+a[u].x;
if(!n) ans[u]=C(s[n].x);
else
{
int cur=0,j=f[n];
for(int tj=0;~j;j=tj)
{
if(j<f[n] && f[j]>j/2)
tj=(j-1)%(j-f[j])+1;
else tj=f[j];
if(s[j].c==s[n].c)
{
int i=min(s[j].x,s[n].x);
if(cur<i) ans[u]+=C(len[j]+i+1)
-C(len[j]+cur+1),cur=i;
}
if(s[j].x==s[n].x && s[j].c==s[n].c)
break;
}
if(j==-1 && s[0].c==s[n].c && s[0].x<s[n].x)
ans[u]+=s[0].x*(s[n].x-cur),j=0;
f[n+1]=j+1;
}
for(int v:g[u])
ans[v]=ans[u],dfs(v,n+1);
}
signed main()
{
n=read();
for(int i=1;i<=n;i++)
{
int op=read(),x=read();char c;
if(op==1)
{
cin>>c;
a[++m]={x,c-'a'};id[i]=m;
g[id[i-1]].push_back(id[i]);
}
else id[i]=id[x];
}
f[0]=-1;f[1]=0;dfs(0,-1);
for(int i=1;i<=n;i++)
printf("%lld\n",ans[id[i]]%MOD);
}
标签:JOJO,int,tt,tj,kmp,HNOI2019,border,id 来源: https://www.cnblogs.com/C202044zxy/p/16368947.html