Jzoj5665 奥立卡的诗

227 篇文章 3 订阅
134 篇文章 0 订阅

终于又遇到SAM的题了好好玩,而且就这道题让我弄清楚了广义SAM和Trie上SAM的区别

其实两者是没有多少区别的,不过Trie上SAM可以更快

关于Trie上SAM,是用bfs的方法来构建的,相比起广义SAM用dfs建少了一个深度之和的部分

但是如果原题给的就是Trie那么就只能用bfs了,因为dfs会被卡成O(n^2) (考虑一个扫把)

回到本题,简化一下式子发现这道题求的是不同子串的出现次数的平方和

我们建出整个诗的广义SAM,对于一次询问,

我们将一个串每个前缀对应的节点size都加一,树链剖分来维护所有节点的权值平方和

让后就愉快的过了

#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 600010
#define LL long long
#define mid (l+r>>1)
#define ls l,mid,x<<1
#define rs mid+1,r,x<<1|1
using namespace std;
char C[N]; LL ans=0;
struct edge{ int v,nt; } G[N<<1];
int n,m,cnt,clk,h[N],f[N],d[N],sz[N];
int top[N],w[N],l[N],son[N],dx[N]; LL s2[N<<2],s[N<<2],t[N<<2],c[N<<2];
inline void ps(int x){ 
	s[x]=s[x<<1]+s[x<<1|1];
	s2[x]=s2[x<<1]+s2[x<<1|1];
}
inline void adj(int x,int y){
	G[++cnt]=(edge){y,h[x]}; h[x]=cnt;
	G[++cnt]=(edge){x,h[y]}; h[y]=cnt;
}
inline void dfs(int x,int p){
	f[x]=p; d[x]=d[p]+1; sz[x]=1;
	for(int v,i=h[x];i;i=G[i].nt)
		if(!d[v=G[i].v]){
			dfs(v,x);
			sz[x]+=sz[v];
			if(sz[v]>sz[son[x]]) son[x]=v;
		}
}
inline void dgs(int x,int p){
	w[++clk]=x; l[x]=clk; top[x]=p;
	if(son[x]) dgs(son[x],p);
	for(int v,i=h[x];i;i=G[i].nt)
		if(!top[v=G[i].v]) dgs(v,v);
}
inline void build(int l,int r,int x){
	if(l==r){ c[x]=dx[w[l]]; return; }
	build(ls);
	build(rs);
	c[x]=c[x<<1]+c[x<<1|1];
}
inline void pd(int x){
	if(t[x]){
		t[x<<1]+=t[x];
		t[x<<1|1]+=t[x];
		s2[x<<1]+=t[x]*t[x]*c[x<<1]+2*t[x]*s[x<<1];
		s2[x<<1|1]+=t[x]*t[x]*c[x<<1|1]+2*t[x]*s[x<<1|1];
		s[x<<1]+=c[x<<1]*t[x];
		s[x<<1|1]+=c[x<<1|1]*t[x];
		t[x]=0;
	}
}
inline void add(int l,int r,int x,int L,int R){
	if(L<=l && r<=R){
		s2[x]+=c[x]+2*s[x];
		s[x]+=c[x]; t[x]++; return;
	}
	pd(x);
	if(L<=mid) add(ls,L,R);
	if(mid<R) add(rs,L,R);
	ps(x);
}
inline void gLca(int x){
	for(;x;x=f[top[x]])
		add(1,n,1,l[top[x]],l[x]);
}
struct SAM{
	int s[N][26],mx[N],f[N],cnt,lst;
	SAM(){ cnt=lst=1; }
	inline int extend(int c){
		int p=lst,np=lst=++cnt,q,nq;
		mx[np]=mx[p]+1;
		for(;p&&!s[p][c];p=f[p]) s[p][c]=np;
		if(!p) return f[np]=1;
		q=s[p][c];
		if(mx[q]==mx[p]+1) f[np]=q;
		else{
			nq=++cnt;
			mx[nq]=mx[p]+1;
			f[nq]=f[q]; f[q]=f[np]=nq;
			memcpy(s[nq],s[q],26<<2);
			for(;p&&s[p][c]==q;p=f[p]) s[p][c]=nq;
		}
	}
	inline void Ex_extend(int c){
		int p=lst,q=s[p][c],nq;
		if(q){
			if(mx[q]==mx[p]+1){ lst=q; }
			else{
				lst=nq=++cnt;
				mx[nq]=mx[p]+1;
				f[nq]=f[q]; f[q]=nq;
				memcpy(s[nq],s[q],26<<2);
				for(;p&&s[p][c]==q;p=f[p]) s[p][c]=nq;
			}
		} else extend(c);
	}
	inline void BT(){
		for(int i=2;i<=cnt;++i){
			adj(f[i],i);
			dx[i]=mx[i]-mx[f[i]];
		}
	}
} S;
int main(){
	freopen("poem.in","r",stdin);
	freopen("poem.out","w",stdout);
	scanf("%d",&n);
	for(int j=0;n--;++j){
		scanf("%s",C+j); S.lst=1;
		for(;C[j];++j) S.Ex_extend(C[j]-'a');
	}
	n=S.cnt; S.BT(); dfs(1,0); dgs(1,1); build(1,n,1);
	for(int x=1,j=0;C[j];++j){
		x=1;
		while(C[j]){
			x=S.s[x][C[j]-'a'];
			gLca(x); ++j;
		}
		printf("%lld\n",s2[1]);
	}
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值