[HNOI2019]JOJO
不错的好题
有一个比较弱化的问题:
已知小串的子串问题,例题2
本题:
首先单纯快速跳kmp就要用到AC自动机的fail了(单串AC自动机)
所谓回撤,就是一个可持久化,
建出时间树,直接dfs,回来的时候撤销这一路的操作。
问题就是往S后插入x个c,考虑新加入的fail之和
我们把(x,c)尽量看成一个整体 (请感性理解这句话23333)
不妨称一个操作(边)为(x,c)
这里一个重要条件:每次的x个c一定是极长的,也就是当前结尾不是c字符
那么我们的fail[u]只用保留一个节点,而不是“边”,一定是一个(x,c)的结束位置
支持找失配点,我们要维护fail
手玩发现,最后一个(x,c)其实不太好维护
那么考虑fail树链上的前面部分的答案。
再处理新加入(x,c)的每个插入[1,x]个字符单独产生的贡献
(后面所有的处理都是基于这个的)
每个点2*26棵主席树,
1.支持查询答案,我们用一组26棵主席树rt1[u][c],叶子节点x权值就是假如插入了(x,c),整个u在fail树路径上,有(x,c)出边的、匹配的最长长度
2.第二棵主席树就是fail的出边
而根据“那么我们的fail[u]只用保留一个节点,而不是“边”,一定是一个(x,c)的结束位置”
所以,第二棵主席树rt2[u][c]的节点x权值是:u的fail树祖先上,第一个恰好有(x,c)出边,这个(x,c)出边指向的儿子节点
3.为了统计答案的时候 统计单独产生的贡献,我们还需要维护mx[u][c]表示,u节点从fail树祖先过来,的c字符出边最长的x是多少。
(好绕啊。。。。)
对于u节点后面插入v节点,边是(x,c)
要做如下事情:
查询新增的答案
1.查询rt1[u][c]的(1~x)的和
2.令t=min(x,mx[u][c]),ans+=t*(t+1)/2,这个是(x,c)单独贡献的
3.当mx[u][c]更小的时候,一个问题:
这个时候意味着红色这个全是x组成的后缀并没有单独做出贡献
但是假设当前开始也是c这个字符,长度为sx,那么每一个都可以和这个红色部分结尾的前缀进行一次kmp的贡献
也就是,ans+=(x-t)*sx(因为这个时候,x一定比sx大)
更新?
关于u的
1.mx[u][c]=max(mx[u][c],x)
2.rt1[u][c].upda(1,x,len[u])(len表示当前串到u节点的长度)
3.rt2[u][c].upda(x,v)如果之后某个点的fail是u,并且有一个(x,c)的出边,那么v可以直接作为该出边节点的fail
关于v的:
1.fail[v]的维护?令fa=rt2[u][c].x,
如果fa不是0,那么fail[v]=fa
如果fa是0,还是有这种可能:
假设开始的字符确实是x,并且sx<=x
那么,可以直接把这第一个出点firstcur,作为fail[v]
否则,fail[v]=0
2.rt1,rt2,mx都从fail[v]继承过来
然后就没了
(是不是很麻烦)
其实都是围绕
那么考虑fail树链上的前面部分的答案。
再处理新加入(x,c)的每个插入[1,x]个字符单独产生的贡献
展开的,对于维护和统计答案还需要维护别的东西而已
出错点:
1.len是+x的
2.输出回车换行。。
3.还原时候,u=0,要直接赋值为0
4.多pushup了一次(没有pushdown的时候不能pushup!)
// luogu-judger-enable-o2 #include<bits/stdc++.h> #define reg register int #define il inline #define fi first #define se second #define mk(a,b) make_pair(a,b) #define numb (ch^'0') #define pb push_back #define solid const auto & #define enter cout<<endl #define pii pair<int,int> using namespace std; typedef long long ll; template<class T>il void rd(T &x){ char ch;x=0;bool fl=false; while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true); for(x=numb;isdigit(ch=getchar());x=x*10+numb); (fl==true)&&(x=-x); } template<class T>il void output(T x){if(x/10)output(x/10);putchar(x%10+'0');} template<class T>il void ot(T x){if(x<0) putchar('-'),x=-x;output(x);putchar(' ');} template<class T>il void prt(T a[],int st,int nd){for(reg i=st;i<=nd;++i) ot(a[i]);putchar('\n');} namespace Miracle{ const int N=100000+5; const int mod=998244353; const int lim=10000; #define mid ((l+r)>>1) int n; int ans[N]; int to[N]; struct edge{ int nxt,to; int x,c; }e[N]; int hd[N],cnt; void add(int u,int v,int ct,int c){ e[++cnt].nxt=hd[u]; e[cnt].to=v; e[cnt].c=c; e[cnt].x=ct; hd[u]=cnt; } struct node{ int ls,rs; int nxt,val; int tag; node(){ ls=0;rs=0;nxt=0;val=0;tag=-1; } }t[N*80]; int tot; int rt[N][26]; int sx,sc,fc; int mx[N][26]; int fail[N]; int ad(int x,int y){ return x+y>=mod?x+y-mod:x+y; } void pushup(int x){ t[x].val=ad(t[t[x].ls].val,t[t[x].rs].val); } int cpy(int cur){ ++tot;t[tot]=t[cur];return tot; } void tag(int &x,int l,int r,int c){ // cout<<" tag "<<c<<endl; x=cpy(x); t[x].tag=c; t[x].val=(ll)c*(r-l+1)%mod; } void pushdown(int x,int l,int r){ if((!x)||(t[x].tag==-1)) return; tag(t[x].ls,l,mid,t[x].tag); tag(t[x].rs,mid+1,r,t[x].tag); t[x].tag=-1; } void chan(int &x,int y,int l,int r,int L,int R,int c){ if(!x){ x=cpy(y); } // cout<<" chan "<<" x "<<x<<" : "<<l<<" "<<r<<" L "<<L<<" R "<<R<<" c "<<c<<" val "<<t[x].val<<endl; if(L<=l&&r<=R){ t[x].tag=c; t[x].val=(ll)c*(r-l+1)%mod; return; } pushdown(x,l,r); if(L<=mid){ t[x].ls=cpy(t[x].ls);chan(t[x].ls,t[y].ls,l,mid,L,R,c); } if(mid<R){ t[x].rs=cpy(t[x].rs);chan(t[x].rs,t[y].rs,mid+1,r,L,R,c); } pushup(x); } void upda(int &x,int y,int l,int r,int p,int to){ if(!x){ x=cpy(y); } if(l==r){ t[x].nxt=to;return; } // pushdown(x,l,r);//dele ? //warning!! no pushdown if(p<=mid){ t[x].ls=cpy(t[x].ls);upda(t[x].ls,t[y].ls,l,mid,p,to); }else{ t[x].rs=cpy(t[x].rs);upda(t[x].rs,t[y].rs,mid+1,r,p,to); } // pushup(x); } int query(int x,int l,int r,int L,int R){ // cout<<" query "<<" x "<<x<<" : "<<l<<" "<<r<<" L "<<L<<" R "<<R<<" val "<<t[x].val<<endl; if(L<=l&&r<=R){ return t[x].val; } pushdown(x,l,r); if(R<=mid) return query(t[x].ls,l,mid,L,R); if(mid<L) return query(t[x].rs,mid+1,r,L,R); return ad(query(t[x].ls,l,mid,L,R),query(t[x].rs,mid+1,r,L,R)); } int fin(int x,int l,int r,int p){ if(l==r) return t[x].nxt; // pushdown(x,l,r);//dele? if(p<=mid) return fin(t[x].ls,l,mid,p); else return fin(t[x].rs,mid+1,r,p); } void dfs(int u,int len){ // cout<<" dfs "<<u<<" len "<<len<<endl; // cout<<" fail "<<fail[u]<<endl; //5 for(reg j=0;j<26;++j) mx[u][j]=mx[fail[u]][j]; //6 for(reg i=0;i<26;++i){ rt[u][i]=rt[fail[u]][i]; } for(reg o=hd[u];o;o=e[o].nxt){ int v=e[o].to; if(u==0) sx=e[o].x,sc=e[o].c,fc=v; int x=e[o].x,c=e[o].c; // cout<<" xx "<<x<<" cc "<<c<<endl; ans[v]=ans[u]; //1 int t=min(x,mx[u][c]); // cout<<" tt "<<t<<" rt "<<rt[u][c]<<" fai "<<rt[fail[u]][c]<<endl; int lp=query(rt[u][c],1,lim,1,x); // cout<<" lp "<<lp<<endl; lp=ad((ll)t*(t+1)/2%mod,lp); ans[v]=ad(ans[v],lp); if(t<x&&sc==c){//has nxt=0 int re=x-t; if(u==0){ ans[v]=ad(ans[v],(ll)x*(x-1)/2%mod); }else{ ans[v]=ad(ans[v],(ll)re*sx%mod); } } //3 int fa=fin(rt[fail[u]][c],1,lim,x); if(fa!=0){ fail[v]=fa; }else{ // cout<<" u "<<u<<" || "<<sc<<" "<<c<<" || "<<x<<" "<<sx<<endl; if(u&&sc==c&&x>=sx){ fail[v]=fc; }else{ fail[v]=0; } } //2.1 int tmp=rt[u][c]; rt[u][c]=0; chan(rt[u][c],tmp,1,lim,1,x,len); //2.2 tmp=rt[u][c]; rt[u][c]=0; upda(rt[u][c],tmp,1,lim,x,v); //4 int od=mx[u][c]; mx[u][c]=max(mx[u][c],x); dfs(v,len+x); //warning 1 if(u){ rt[u][c]=rt[fail[u]][c]; mx[u][c]=od; }else{ rt[u][c]=0;mx[u][c]=0; } } } int main(){ rd(n); int now=0; int op,x;char c[233]; for(reg i=1;i<=n;++i){ rd(op); if(op==1){ to[i]=i; rd(x);scanf("%s",c+1); // cout<<c+1<<endl; add(now,i,x,c[1]-'a'); now=i; }else{ rd(x); to[i]=to[x]; now=to[x]; } } dfs(0,0); // prt(ans,1,n); // prt(to,1,n); for(reg i=1;i<=n;++i){ ans[i]=ans[to[i]]; printf("%d\n",ans[i]); } return 0; } } signed main(){ Miracle::main(); return 0; } /* Author: *Miracle* */