51nod 1600 Simple KMP

题意

考虑转化下(f(S))
我们考虑(S)(fail)树上的每一个点(x),如果(x)(0)的路径上有(cnt)个点(除去(x)(0)),那么就会对(f(S))产生(cnt)的贡献,于是现在(f(S))变为了每个点会对很多点产生(1)的贡献,最后求和。
我们知道每个点代表一个前缀,那么我们就可以转化成每个前缀的贡献若干个(1)。易知,如果一个前缀在(S)中出现了(cnt)次(除了它本身),那么就会产生(cnt)的贡献。

我们再来想(key(S))
(key(S)=sumlimits_{S'in S}f(S'))

我们统计每个子串的贡献,假如有两个子串(S_{l1,r2},S_{l2,r2}(l1<l2))相等,那么就会产生(n-r2)的贡献。

但这样还是不好算,但是题中已经给我们提示了,我们是不断向后添加字符的,每次会新添加一个(endpos),算出增量即可。

每次的增量(sum_i)
(sum_i=sum_{i-1}+calc(i))
(calc(i))表示从(endpos_i)(endpos)集合中只包含(i)的点)到根((1))的这条链上,所有的串都要贡献(1),我们求个和。
(sum_{i-1})是上一次的增量,因为我们向后添加了一个字符,所以之前所有的串的贡献都要(+1)

树剖/(LCT)维护即可。

code:

#include<bits/stdc++.h>
using namespace std;
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
const int maxn=2e5+10;
const int mod=1e9+7;
int n,ans;
int endpos[maxn];
char s[maxn];
inline int read()
{
	char c=getchar();int res=0,f=1;
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9')res=res*10+c-'0',c=getchar();
	return res*f;
}
inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
struct SAM
{
	int last,tot;
	int fa[maxn],len[maxn];
	int ch[maxn][26];
	SAM(){last=tot=1;}
	inline void add(int c)
	{
		int now=++tot;len[now]=len[last]+1;
		int p=last;last=now;
		while(p&&!ch[p][c])ch[p][c]=now,p=fa[p];
		if(!p){fa[now]=1;return;}
		int q=ch[p][c];
		if(len[q]==len[p]+1)fa[now]=q;
		else 
		{
			int nowq=++tot;len[nowq]=len[p]+1;
			memcpy(ch[nowq],ch[q],sizeof(ch[q]));
			fa[nowq]=fa[q],fa[q]=fa[now]=nowq;
			while(p&&ch[p][c]==q)ch[p][c]=nowq,p=fa[p];
		}
	}
}sam;
int cnt_edge;
int head[maxn];
struct edge{int to,nxt;}e[maxn<<1];
inline void add_edge(int u,int v)
{
	e[++cnt_edge]=(edge){v,head[u]};
	head[u]=cnt_edge;
}
int size[maxn],son[maxn],dep[maxn],pre[maxn];
void dfs1(int x,int fa)
{
	size[x]=1;
	dep[x]=dep[pre[x]=fa]+1;
	for(int i=head[x];i;i=e[i].nxt)
	{
		int y=e[i].to;
		if(y==fa)continue;
		dfs1(y,x);size[x]+=size[y];
		if(size[y]>size[son[x]])son[x]=y;
	}
}
int tim;
int dfn[maxn],pos[maxn],top[maxn];
void dfs2(int x,int tp)
{
	pos[dfn[x]=++tim]=x;
	top[x]=tp;
	if(son[x])dfs2(son[x],tp);
	for(int i=head[x];i;i=e[i].nxt)
	{
		int y=e[i].to;
		if(y==son[x]||y==pre[x])continue;
		dfs2(y,y);
	}
}
struct Segment_tree
{
	#define len(p) (seg[p].len)
	#define sum(p) (seg[p].sum)
	#define tag(p) (seg[p].tag)
	int len,sum,tag;
}seg[maxn<<2];
void build(int p,int l,int r)
{
	if(l==r){len(p)=sam.len[pos[l]]-sam.len[sam.fa[pos[l]]];return;}
	int mid=(l+r)>>1;
	build(ls(p),l,mid);build(rs(p),mid+1,r);
	len(p)=add(len(ls(p)),len(rs(p)));
}
inline void down(int p)
{
	if(!tag(p))return;
	int k=tag(p);
	sum(ls(p))=add(sum(ls(p)),1ll*k*len(ls(p))%mod);
	sum(rs(p))=add(sum(rs(p)),1ll*k*len(rs(p))%mod);
	tag(ls(p))=add(tag(ls(p)),k),tag(rs(p))=add(tag(rs(p)),k);
	tag(p)=0;
}
void change(int p,int l,int r,int ql,int qr)
{
	if(l>=ql&&r<=qr)
	{
		sum(p)=add(sum(p),len(p));
		tag(p)++;
		return;
	}
	down(p);
	int mid=(l+r)>>1;
	if(ql<=mid)change(ls(p),l,mid,ql,qr);
	if(qr>mid)change(rs(p),mid+1,r,ql,qr);
	sum(p)=add(sum(ls(p)),sum(rs(p)));
}
int query(int p,int l,int r,int ql,int qr)
{
	if(l>=ql&&r<=qr)return sum(p);
	down(p);
	int mid=(l+r)>>1,res=0;
	if(ql<=mid)res=add(res,query(ls(p),l,mid,ql,qr));
	if(qr>mid)res=add(res,query(rs(p),mid+1,r,ql,qr));
	return res;
}
inline void change(int x)
{
	while(top[x]!=1)change(1,1,sam.tot,dfn[top[x]],dfn[x]),x=pre[top[x]];
	change(1,1,sam.tot,dfn[1],dfn[x]);
}
inline int query(int x)
{
	int res=0;
	while(top[x]!=1)res=add(res,query(1,1,sam.tot,dfn[top[x]],dfn[x])),x=pre[top[x]];
	res=add(res,query(1,1,sam.tot,dfn[1],dfn[x]));
	return res;
}
int main()
{
	n=read();
	scanf("%s",s+1);
	for(int i=1;i<=n;i++)sam.add(s[i]-'a'),endpos[i]=sam.last;
	for(int i=1;i<=sam.tot;i++)add_edge(sam.fa[i],i),add_edge(i,sam.fa[i]);
	dfs1(1,0),dfs2(1,1);
	build(1,1,sam.tot);
	int sum=0;
	for(int i=1;i<=n;i++)
	{
		sum=add(sum,query(endpos[i]));
		change(endpos[i]);
		ans=add(ans,sum);
		printf("%d
",ans);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/nofind/p/13067930.html