LGP8203题解

首先我们对所有的 \(s\) 建出 ACAM,然后把所有 \(s\) 的信息丢到上去。问题就变为了每个串 \(t\) 对应一个大小为 \(|t|\) 的点集,求两个点集的虚树的交的点权和。

虚树交这种东西看上去比较奇怪,考虑根号分治。

首先我们先使用 \(O(n\log n)-O(1)\) 的 LCA。

考虑三种情况:大对大,小对大,小对小。

小对大很容易啊。直接把大的专门建一颗树,然后把所有 \(s\) 的信息插上去。

对于每次查询一个小的串,直接在这颗树上面跑虚树就完了。

接下来考虑小对小。

可以建出虚树的并,然后标记每个节点是否包含第一个和第二个串,然后大力统计。

或者,也可以按照 dfn 序排序后大力归并。

大概就是说,对于一个节点 \(u\),如果在这个点集的虚树上最深的被标记的祖先是 \(z\) 的话,相当于要在这条路径上找一个最深的节点能被另外一个点集的虚树所包含。

可以发现归并的时候随随便便求一下就完了(

大对大。。。用两种方法预处理都可以。

根号分治的长度为 \(\sqrt{\sum|S|}\),复杂度 \(O(\sum|S|\log\sum|S|+(m+\sum|S|)\sqrt{\sum|S|})\)

不过我们需要求的是虚树的点权和而不是虚树本身,所以连虚树都可以不用写(((

#include<algorithm>
#include<cstring>
#include<cstdio>
#include<vector>
#include<cmath>
typedef unsigned ui;
const ui M=1e5+5;
ui n,m,Q,B,lg[M<<1];char S[M],T[M],*s[M],*t[M];ui lens[M],lent[M];ui sums,sumt;
ui tot(1),fail[M],trans[M][26];ui dfc,d[M],dfn[M],lca[19][M<<1];
ui cnt,h[M];std::vector<ui>point[M];ui v[M];
ui LEN,id[M],ans[318][318];ui L,R,q[M];
struct Edge{
	ui v,nx;
}e[M];
inline void Add(const ui&u,const ui&v){
	e[++cnt]=(Edge){v,h[u]};h[u]=cnt;
}
inline bool cmp(const ui&u,const ui&v){
	return dfn[u]<dfn[v];
}
inline void Insert(char*s,const ui&n){
	ui u(1);
	for(ui i=0;i<n;++i){
		const ui&c=s[i]-97;
		if(!trans[u][c])trans[u][c]=++tot;u=trans[u][c];
	}
}
inline ui Match(char*s,const ui&n){
	ui u(1);for(ui i=0;i<n;++i)u=trans[u][s[i]-97];return u;
}
inline void Find(char*s,const ui&n,std::vector<ui>&point){
	ui u(1);for(ui i=0;i<n;++i)point.push_back(u=trans[u][s[i]-97]);std::sort(point.begin(),point.end(),cmp);
}
inline void DFS(const ui&u){
	lca[0][dfn[u]=++dfc]=u;d[u]=d[fail[u]]+1;for(ui E=h[u];E;E=e[E].nx)DFS(e[E].v),lca[0][++dfc]=u;
}
inline ui Merge(const ui&u,const ui&v){
	return d[u]>d[v]?v:u;
}
inline ui merge(const ui&u,const ui&v){
	return d[u]>d[v]?u:v;
}
inline ui LCA(ui u,ui v){
	if((u=dfn[u])>(v=dfn[v]))u^=v^=u^=v;
	const ui&k=lg[v-u+1];return Merge(lca[k][u],lca[k][v-(1<<k)+1]);
}
inline void Build(){
	L=1;
	for(ui c=0;c<26;++c){
		if(trans[1][c])q[++R]=trans[1][c],fail[trans[1][c]]=1;
		else trans[1][c]=1;
	}
	while(L<=R){
		const ui&u=q[L++];
		for(ui c=0;c<26;++c){
			if(trans[u][c])q[++R]=trans[u][c],fail[trans[u][c]]=trans[fail[u]][c];
			else trans[u][c]=trans[fail[u]][c];
		}
	}
	for(ui u=1;u<=tot;++u)Add(fail[u],u);DFS(1);
	lg[0]=-1;for(ui i=1;i<=dfc;++i)lg[i]=lg[i>>1]+1;
	for(ui i=1;(1<<i)<=dfc;++i)for(ui j=1;j+(1<<i)-1<=dfc;++j){
		lca[i][j]=Merge(lca[i-1][j],lca[i-1][j+(1<<i-1)]);
	}
}
inline std::vector<ui>Merge(std::vector<ui>S,std::vector<ui>T){
	static std::vector<ui>ans;ui v,id(0);std::vector<ui>().swap(ans);
	for(ui i=0;i<S.size()-1;++i){
		v=1;
		for(ui j=id;j<T.size()&&dfn[T[j]]<dfn[S[i+1]];++j)v=merge(v,LCA(S[i],T[j]));
		if(v!=1)ans.push_back(v);while(id<T.size()&&dfn[T[id]]<dfn[S[i]])++id;
	}
	for(v=1;id!=T.size();++id)v=merge(v,LCA(S[S.size()-1],T[id]));if(v!=1)ans.push_back(v);
	return ans;
}
struct Tree{
	ui sum[M];bool vis[M];
	inline void DFS(const ui&u){
		sum[u]+=sum[fail[u]];for(ui E=h[u];E;E=e[E].nx)DFS(e[E].v);
	}
	inline void init(std::vector<ui>S){
		for(ui&u:S)vis[u]=true;
		for(ui i=R;i>=1;--i)vis[fail[q[i]]]|=vis[q[i]];
		for(ui i=1;i<=tot;++i)if(!vis[i])sum[i]=0;
		for(ui i=1;i<=R;++i)sum[q[i]]+=sum[fail[q[i]]];
	}
	inline ui Qry(std::vector<ui>S){
		ui ans=sum[1],v=1;for(ui&u:S)ans+=sum[u]-sum[LCA(v,u)],v=u;return ans;
	}
}tree[317];
signed main(){
	scanf("%u%u%u",&n,&m,&Q);s[0]=S;t[0]=T;
	for(ui i=1;i<=n;++i)scanf("%s",s[i]=s[i-1]+lens[i-1]),lens[i]=strlen(s[i]),sums+=lens[i];
	for(ui i=1;i<=m;++i)scanf("%s",t[i]=t[i-1]+lent[i-1]),lent[i]=strlen(t[i]),sumt+=lent[i];
	for(ui i=1;i<=n;++i)Insert(s[i],lens[i]);Build();B=ceil(sqrt(sumt));
	for(ui i=1;i<=m;++i)if(lent[i]>=B)id[v[i]=++LEN]=i;
	for(ui i=1;i<=m;++i)Find(t[i],lent[i],point[i]);
	for(ui u=1;u<=tot;++u)point[0].push_back(u);
	for(ui i=1;i<=n;++i){
		const ui&u=Match(s[i],lens[i]);
		for(ui k=0;k<=LEN;++k)++tree[k].sum[u];
	}
	for(ui i=0;i<=LEN;++i)tree[i].init(point[id[i]]);
	for(ui i=1;i<=LEN;++i)for(ui j=i+1;j<=LEN;++j){
		if(lent[id[i]]>lent[id[j]])ans[i][j]=ans[j][i]=tree[i].Qry(point[id[j]]);
		else ans[i][j]=ans[j][i]=tree[j].Qry(point[id[i]]);
	}
	while(Q--){
		ui x,y;scanf("%u%u",&x,&y);if(lent[x]>lent[y])x^=y^=x^=y;
		if(v[x]&&v[y])printf("%u\n",ans[v[x]][v[y]]);
		else if(!v[x]&&v[y])printf("%u\n",tree[v[y]].Qry(point[x]));
		else printf("%u\n",tree[0].Qry(Merge(point[y],point[x])));
	}
}
posted @ 2022-03-29 08:13  Prean  阅读(16)  评论(0编辑  收藏  举报
var canShowAdsense=function(){return !!0};