正题
题目链接:https://www.luogu.com.cn/problem/P5287
题目大意
开始一个空串,nnn个操作
- 在末尾加入xxx个ccc字符(保证和ccc和前面的字符不同)
- 返回到第xxx次操作之后
每次操作完成后求所有前缀的最长的borderborderborder长度和
1≤n≤1051\leq n\leq 10^51≤n≤105
解题思路
二操作好像是一个离线树能搞出来的先只考虑一操作,因为是相当于求kmpkmpkmp之后的nextnextnext和,所以可以考虑一下用kmpkmpkmp。
每个插入操作我们可以看做插入了一个二元组(c,x)(c,x)(c,x),nxtinxt_inxti表示按照二元组完全匹配来KMP的nextnextnext数组。
然后求答案的时候设现在这个二元组(c,x)(c,x)(c,x),已经匹配到nownownow个ccc,然后往前跳nxtnxtnxt的时候如果下一个二元组的字符是ccc且数量x>nowx>nowx>now那么我们就往后计算答案然后让now=xnow=xnow=x。
然后最后剩下的一些如果能和第一个匹配就全都匹配。
然后这样跑是能过的,但是加了二操作之后就会TTT,因为KMP是均摊复杂度的,会被hackhackhack。
所以考虑一下怎么优化,我们知道broderbroderbroder可以被划分成logloglog个等差数列,而且一定是按照长度来排列的,如果我们发现我们跳进了一个无法匹配的等差数列中我们就直接跳到这个等差数列结束的位置因为这个等差数列中已经不能匹配了。
这样复杂度就到了O(nlogn)O(n\log n)O(nlogn)了
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;
}