P5287-[HNOI2019]JOJO【KMP】
阿新 • • 發佈:2021-06-21
正題
題目連結:https://www.luogu.com.cn/problem/P5287
題目大意
開始一個空串,\(n\)個操作
- 在末尾加入\(x\)個\(c\)字元(保證和\(c\)和前面的字元不同)
- 返回到第\(x\)次操作之後
每次操作完成後求所有字首的最長的\(border\)長度和
\(1\leq n\leq 10^5\)
解題思路
二操作好像是一個離線樹能搞出來的先只考慮一操作,因為是相當於求\(kmp\)之後的\(next\)和,所以可以考慮一下用\(kmp\)。
每個插入操作我們可以看做插入了一個二元組\((c,x)\),\(nxt_i\)表示按照二元組完全匹配來KMP的\(next\)
然後求答案的時候設現在這個二元組\((c,x)\),已經匹配到\(now\)個\(c\),然後往前跳\(nxt\)的時候如果下一個二元組的字元是\(c\)且數量\(x>now\)那麼我們就往後計算答案然後讓\(now=x\)。
然後最後剩下的一些如果能和第一個匹配就全都匹配。
然後這樣跑是能過的,但是加了二操作之後就會\(T\),因為KMP是均攤複雜度的,會被\(hack\)。
所以考慮一下怎麼優化,我們知道\(broder\)可以被劃分成\(log\)個等差數列,而且一定是按照長度來排列的,如果我們發現我們跳進了一個無法匹配的等差數列中我們就直接跳到這個等差數列結束的位置因為這個等差數列中已經不能匹配了。
這樣複雜度就到了\(O(n\log n)\)了
code
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<vector> #define ll long long using namespace std; const ll N=1e5+10,P=998244353; ll n,len,ch[N][2],s[N][3],ans[N],nxt[N],prt[N],p[N]; vector<ll> e[N]; ll gs(ll l,ll r) {return (r+l)*(r-l+1)/2;} void dfs(ll x){ ll i=nxt[len++]; s[len][0]=ch[x][0]; s[len][1]=ch[x][1]; s[len][2]=s[len-1][2]+ch[x][1]; ans[len]=ans[len-1];nxt[len]=0; if(len==1){ans[len]=gs(1,ch[x][1]-1);} else{ ll d=len-i; while(i&&(s[i+1][0]!=ch[x][0]||s[i+1][1]!=ch[x][1])){ if(i-nxt[i]==d)i=i%d+d; d=i-nxt[i];i=nxt[i]; } nxt[len]=i+(i||(ch[x][0]==s[1][0]&&ch[x][1]>=s[1][1])); ll now=0;i=nxt[len-1],d=len-1-i; while(now<ch[x][1]&&i){ if(s[i+1][0]==ch[x][0]&&s[i+1][1]>now){ ans[len]+=gs(s[i][2]+now+1,s[i][2]+min(ch[x][1],s[i+1][1])); now=s[i+1][1]; } if(i-nxt[i]==d)i=i%d+d; d=i-nxt[i];i=nxt[i]; } if(now<ch[x][1]&&s[1][0]==ch[x][0]){ if(s[i+1][1]>now)ans[len]+=gs(now+1,min(ch[x][1],s[i+1][1])); now=max(now,min(ch[x][1],s[i+1][1])); ans[len]+=s[1][1]*(ch[x][1]-now); } } prt[x]=ans[len]; for(ll i=0;i<e[x].size();i++) dfs(e[x][i]); len--;return; } signed main() { scanf("%lld",&n); for(ll i=1;i<=n;i++){ ll op;scanf("%lld ",&op); if(op==1){ scanf("%lld %c",&ch[i][1],&ch[i][0]); e[p[i-1]].push_back(p[i]=i); } else{ ll x;scanf("%lld",&x); p[i]=p[x]; } } for(ll i=0;i<e[0].size();i++) dfs(e[0][i]); for(ll i=1;i<=n;i++) printf("%lld\n",prt[p[i]]%P); return 0; }