【HNOI2019】JOJO(KMP)(Border理论)

传送门


题解:

首先,由于没有强制在线,我们可以把操作离线下来在操作树上dfs搞一波。

显然就是对字符串求一个kmp并算 ∑ n x t i \sum nxt_i nxti。考虑对于这个压缩了的串怎么处理。

求nxt的时候,我们把二元组看作是一个特殊字符,求出这个意义下的nxt,因为之后再在后面加字符的时候也不会有重复,所以要连上必须是完全匹配的压缩串。

然后再单独跑一遍,将这个新加的压缩段匹配到前面的若干个位置,贡献可以看成是一个等差数列的形式,每次有匹配的地方需要更新当前压缩段已经匹配了的位置。匹配完成之后就不要再跑了。

由于KMP的复杂度实际上是均摊的,所以这样直接做复杂度是假的。

但是根据jcvb在WC2017讲课时候讲到的字符串理论,我们可以将所有border分为log个等差数列,如果当前字符串存在周期,跳nxt的时候直接跳到周期的第一个即可。

这样单次跳的复杂度就是 O ( log ⁡ n ) O(\log n) O(logn),没有问题了。


代码:

#include
#define ll long long
#define re register
#define gc get_char
#define cs const

namespace IO{
	cs int Rlen=1<<22|1;
	char buf[Rlen],*p1,*p2;
		
	inline char get_char(){return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;}
	inline char peek(){return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1;}
	inline char ga(){while(!isalpha(peek()))gc();return gc();}
	template<typename T>
	inline T get(){
		char c;
		while(!isdigit(c=gc()));T num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
	inline int gi(){return get<int>();}
}
using namespace IO;

using std::cerr;
using std::cout; 

cs int mod=998244353;

inline ll get_sum(int l,int r){
	if(l>r)return 0;
	return (ll)(l+r)*(r-l+1)/2;
}

cs int N=1e5+7;
int n,pos[N];
std::vector<int> G[N];
char op_c[N];int op_l[N],L;
char str[N];int len[N],tot_len[N],nxt[N];
ll ans[N],ans_tr[N];

void dfs(int u,int f){
	if(op_l[u]){
		++L;
		ans[L]=nxt[L]=0;
		char c=str[L]=op_c[u];
		int l=len[L]=op_l[u];
		tot_len[L]=tot_len[L-1]+len[L];
		if(L==1)ans[L]=get_sum(1,len[1]-1);
		else {
			int t=nxt[L-1],gap=L-t;
			while(t&&(str[t+1]!=c||len[t+1]!=l)){
				if(t-nxt[t]==gap)t=t%gap+gap;
				gap=t-nxt[t];
				t=nxt[t];
			}
			if(t||(str[1]==c&&len[1]<=l))nxt[L]=t+1;
			else nxt[L]=t;
			t=nxt[L-1],gap=L-1-t;
			int Len=0;
			while(t&&Len<len[L]){
				if(str[t+1]==c&&len[t+1]>Len){
					ans[L]+=get_sum(tot_len[t]+Len+1,tot_len[t]+std::min(len[t+1],l));
					Len=len[t+1];
				}
				if(t-nxt[t]==gap)t=t%gap+gap;
				gap=t-nxt[t];
				t=nxt[t];
			}
			if(Len<l&&str[1]==c){
				ans[L]+=get_sum(Len+1,std::min(l,len[1]));
				Len=std::max(Len,std::min(l,len[1]));
				ans[L]+=(ll)len[1]*(l-Len);
			}
			ans[L]+=ans[L-1];
		}
	}
	ans_tr[u]=ans[L];
	for(int re v:G[u])dfs(v,u);
	if(op_l[u])--L;
}

std::map<std::pair<char,int>,int> to[N];

signed main(){
#ifdef zxyoi
	freopen("JJ.in","r",stdin);
#endif
	n=gi();
	for(int re i=1;i<=n;++i){
		switch(gi()){
			case 1:{
				op_l[i]=gi();
				op_c[i]=ga();
				std::pair<char,int> p=std::make_pair(op_c[i],op_l[i]);
				pos[i]=to[pos[i-1]][p];
				if(!pos[i])G[pos[i-1]].push_back(pos[i]=to[pos[i-1]][p]=i);
				break;
			}
			case 2:pos[i]=pos[gi()];break;
		}
	}
	dfs(0,-1);
	for(int re i=1;i<=n;++i)cout<<ans_tr[pos[i]]%mod<<"\n";
	return 0;
} 

你可能感兴趣的:(KMP)