• 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;
    }
    
  • 相关阅读:
    SpringBoot读取Resource下文件的几种方式(十五)
    Springboot+vue前后端分离文件上传、下载方式及与Spring的异同(十四)
    springboot多个service互相调用的事务处理(十三)
    Bigdecimal用法
    Linux常见wenti
    informix常见问题
    Spring-boot常见问题(十二)
    浅析VO、DTO、DO、PO的概念、区别和用处(八)
    Tbase读写分离与分库分表
    函数的节流和抖动
  • 原文地址:https://www.cnblogs.com/nofind/p/13067930.html
Copyright © 2020-2023  润新知