神™這題暴力能A,這出題人都沒造那種我考場就想到的數據,難怪我的垃圾做法有分
先考慮沒有撤銷操作怎么做,因為每次插入一段一樣的字符,所以我們可以把\(x\)個字符\(c\)定義為\(cx\),然后用這種新字符做\(\mathrm{kmp}\).但是直接把一般的\(\mathrm{kmp}\)搬過來做是錯的.例如\(yybbbyybb\),最后一個b的\(next\)是第二個b,但是題目有個限制,每次往后加的字符不會和上一個字符相同,那么現在往后加任何字符,因為都不等於b,所以就一定無法繼續匹配.所以,新的\(\mathrm{kmp}\)的\(next\)指向的字符必須和當前字符的長度和字符類型要一致,這樣才能繼續往后接東西.還有一種特殊情況,如果當前字符的類型和第一個相同,但是長度比第一個長,那么\(next\)應該指向1,因為這樣也是可以往后接東西的
然后考慮統計答案,因為這個\(\mathrm{kmp}\)可能會跳過一些匹配,所以我們在暴跳\(next\)的過程中順便統計答案,就是如果\(next\)后面的字符類型和當前相同,答案要加上 當前字符沒算過答案的部分 到那兩個字符長度最小值的這個區間 在\(next\)后面的字符中的到開始位置的長度之和(請感性理解)
然后有撤銷操作,直接上個可持久化我們發現可以把所有操作的串建一個trie樹,然后在上面dfs做,然后撤銷,就可以不用可持久化了qwq
我不可持久化啦!JOJO!
這樣就能獲得100分的好成績(誤
然而\(\mathrm{kmp}\)是均攤\(O(n)\)的,出題人想卡你還是可以卡的.所以我們考慮一種叫\(\mathrm{kmp}\)自動機的東西,就是每個狀態的后繼狀態表示這個狀態后加一個字符,它的\(next\)會指向哪里,每次轉移可以直接把\(next\)設成對應的后繼,然后在把對應的后繼狀態指向當前位置.現在因為新的字符集比較大,所以可以使用可持久化線段樹維護這個\(\mathrm{kmp}\)自動機.但是因為現在是一步跳到\(next\),所以考慮如何統計答案.我們可以手玩統計答案過程,假設串是\(...bbb...bb...\),然后我們會先算上前兩個b在\(bb\)處的長度,然后加上第三個b在\(bbb\)處的長度,如果從前往后看,可以發現這是每次把一個前綴修改成一個元素值更大的等差數列(公差為1),然后答案只要前綴求和,所以這個東西用可持久化線段樹,前綴賦值等差數列以及前綴求和來實現
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<ctime>
#include<queue>
#include<map>
#include<set>
#define LL long long
using namespace std;
const int N=1e5+10,M=2e4,mod=998244353;
int rd()
{
int x=0,w=1;char ch=0;
while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*w;
}
int q,n,to[N*100],ch[N*100][2],rt[N][26],t1;
void inst(int o1,int o2,int x,int y)
{
int l=1,r=n;
while(l<r)
{
int mid=(l+r)>>1;
if(x<=mid)
{
ch[o1][0]=++t1,ch[o1][1]=ch[o2][1];
o1=ch[o1][0],o2=ch[o2][0];
r=mid;
}
else
{
ch[o1][0]=ch[o2][0],ch[o1][1]=++t1;
o1=ch[o1][1],o2=ch[o2][1];
l=mid+1;
}
}
to[o1]=y;
}
int q1(int o,int x)
{
int l=1,r=n;
while(l<r)
{
int mid=(l+r)>>1;
if(x<=mid) o=ch[o][0],r=mid;
else o=ch[o][1],l=mid+1;
}
return to[o];
}
int s[N*100],tg[N*100],ch2[N*100][2],r2[N][26],t2;
int gsm(LL x,LL y){return (1ll*(x+x+y-1)*y/2)%mod;}
void psdn(int o,int l,int r)
{
if(l<r&&tg[o])
{
int mid=(l+r)>>1;
++t2,ch2[t2][0]=ch2[ch2[o][0]][0],ch2[t2][1]=ch2[ch2[o][0]][1],ch2[o][0]=t2,s[ch2[o][0]]=gsm(tg[ch2[o][0]]=tg[o],mid-l+1);
++t2,ch2[t2][0]=ch2[ch2[o][1]][0],ch2[t2][1]=ch2[ch2[o][1]][1],ch2[o][1]=t2,s[ch2[o][1]]=gsm(tg[ch2[o][1]]=tg[o]+mid-l+1,r-mid);
tg[o]=0;
}
}
void modif(int &o,int l,int r,int ll,int rr,LL x)
{
psdn(o,l,r);
int lc=ch2[o][0],rc=ch2[o][1];
++t2,s[t2]=gsm((x+max(l-ll,0))%mod,min(r,rr)-max(l,ll)+1),o=t2;
if(ll<=l&&r<=rr){tg[o]=(x+max(l-ll,0))%mod;return;}
int mid=(l+r)>>1;
ch2[o][0]=lc;
if(ll<=mid) modif(ch2[o][0],l,mid,ll,rr,x);
ch2[o][1]=rc;
if(rr>mid) modif(ch2[o][1],mid+1,r,ll,rr,x);
s[o]=(s[ch2[o][0]]+s[ch2[o][1]])%mod;
}
int quer(int o,int l,int r,int ll,int rr)
{
if(!o) return 0;
psdn(o,l,r);
if(ll<=l&&r<=rr) return s[o];
int mid=(l+r)>>1,an=0;
if(ll<=mid) an+=quer(ch2[o][0],l,mid,ll,rr);
if(rr>mid) an+=quer(ch2[o][1],mid+1,r,ll,rr);
return an%mod;
}
int an[N],sg[26],p[N],nw,aa[N],t0;
vector<int> e[N];
int sta[N],mx[N][26],tp;
LL len[N];
void wk(int x)
{
sta[++tp]=aa[x];
int xx=aa[x]/M,ln=aa[x]%M;
len[tp]=(len[tp-1]+ln)%mod;
int nxt=0;
if(tp==1) an[x]=gsm(1,ln-1);
else
{
nxt=q1(rt[tp][xx],ln);
an[x]=(an[x]+quer(r2[tp][xx],1,n,1,ln))%mod;
if(!nxt&&sta[1]/M==xx&&sta[1]%M<ln) nxt=1,an[x]=(an[x]+1ll*len[1]*max(ln-mx[tp][xx],0)%mod)%mod;
}
int las=rt[tp][xx];
inst(rt[tp][xx]=++t1,las,ln,tp);
modif(r2[tp][xx],1,n,1,ln,len[tp-1]+1);
mx[tp][xx]=max(mx[tp][xx],ln);
int nn=e[x].size();
for(int i=0;i<nn;++i)
{
an[e[x][i]]=an[x];
memcpy(rt[tp+1],rt[nxt+1],sizeof(int)*26),memcpy(r2[tp+1],r2[nxt+1],sizeof(int)*26),memcpy(mx[tp+1],mx[nxt+1],sizeof(int)*26);
wk(e[x][i]);
}
--tp;
}
int main()
{
//awsl
q=rd();
char cc[4];
for(int i=1;i<=q;++i)
{
int op=rd(),x=rd();
if(op==1)
{
n=max(n,x);
scanf("%s",cc);
p[i]=++t0;
e[nw].push_back(t0),aa[t0]=(cc[0]-'a')*M+x,nw=t0;
}
else nw=p[i]=p[x];
}
int nn=e[0].size();
for(int i=0;i<nn;++i)
memset(rt[1],0,sizeof(int)*26),memset(r2[1],0,sizeof(int)*26),memset(mx[1],0,sizeof(int)*26),wk(e[0][i]);
for(int i=1;i<=q;++i) printf("%d\n",an[p[i]]);
return 0;
}
