zoukankan      html  css  js  c++  java
  • Jzoj5665 奥立卡的诗

    终于又遇到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
    ",s2[1]);
    	}
    }


  • 相关阅读:
    一行代码更改博客园皮肤
    fatal: refusing to merge unrelated histories
    使用 netcat 传输大文件
    linux 命令后台运行
    .net core 使用 Nlog 配置文件
    .net core 使用 Nlog 集成 exceptionless 配置文件
    Mysql不同字符串格式的连表查询
    Mongodb between 时间范围
    VS Code 使用 Debugger for Chrome 调试vue
    css权重说明
  • 原文地址:https://www.cnblogs.com/Extended-Ash/p/9477104.html
Copyright © 2011-2022 走看看