[BZOJ3879]SvT(后缀树+虚树)

[BZOJ3879]SvT(后缀树+虚树)

题面

有一个长度为n的仅包含小写字母的字符串S,下标范围为[1,n].
现在有若干组询问,对于每一个询问,我们给出若干个后缀(以其在S中出现的起始位置来表示),求这些后缀两两之间的LCP的长度之和.一对后缀之间的LCP长度仅统计一遍.

分析

建出S的后缀树(实现上直接建反串SAM的parent树). 每一组询问的后缀相当于后缀树上的一个点集。两个后缀在后缀树上对应的节点LCA的len就是他们的LCP长度。因此我们要查询这些点两两之间LCA的len之和。

把这些关键点建出虚树。然后在虚树上做类似树形dp的统计。设(sz_x)表示(x)当前已经合并的子树中关键点的个数,则对于还未被合并的儿子(y),它和已经合并的子树会产生(sz_x cdot sz_y)对点,而这些点对的LCA都是(x)。因此对答案的贡献为(sz_x cdot sz_y cdot len(x))

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#define maxc 26
#define maxn 3000000
#define maxm 3000000
#define mod 23333333333333333ll
using namespace std;
template<typename T> void qread(T &x){
	x=0;
	T sign=1;
	char c=getchar();
	while(c<'0'||c>'9'){
		if(c=='-') sign=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9'){
		x=x*10+c-'0';
		c=getchar();
	}
	x=x*sign; 
} 
template<typename T> void qprint(T x){
	if(x<0){
		putchar('-');
		qprint(-x);
	}else if(x==0){
		putchar('0');
		return;
	}else{
		if(x>=10) qprint(x/10);
		putchar('0'+x%10);
	}
}

int n,m;
char str[maxn+5];
int pos[maxn+5];
vector<int>E[maxn+5];

int qu,q[maxm+5];

struct SAM {
#define len(x) (t[x].len)
#define link(x) (t[x].link)
	struct node {
		int ch[maxc];
		int link;
		int len;
	} t[maxn+5];
	const int root=1;
	int ptr=1;
	int last=root;
	int extend(int c) {
		int p=last,cur=++ptr;
		len(cur)=len(p)+1;
		while(p&&t[p].ch[c]==0) {
			t[p].ch[c]=cur;
			p=link(p);
		}
		if(p==0) link(cur)=root;
		else {
			int q=t[p].ch[c];
			if(len(p)+1==len(q)) link(cur)=q;
			else {
				int clo=++ptr;
				t[clo]=t[q];
				len(clo)=len(p)+1;
				link(q)=link(cur)=clo;
				while(p&&t[p].ch[c]==q) {
					t[p].ch[c]=clo;
					p=link(p);
				}
			}
		}
		last=cur;
		return cur;
	}
	void build(char *s){
		int len=strlen(s+1);
		for(int i=len;i>=1;i--) pos[i]=extend(s[i]-'a');
		for(int i=1;i<=ptr;i++) E[link(i)].push_back(i);
#ifdef DEBUG
		puts("tree:");
		for(int i=1;i<=ptr;i++) printf("%d %d
",link(i),i);
		printf("pos:"); 
		for(int i=1;i<=len;i++) printf("%d ",pos[i]);
		printf("
");
#endif
	}
}S;

int tim;
int dfn[maxm+5],deep[maxm+5],top[maxm+5],son[maxm+5],fa[maxm+5],sz[maxm+5];
void dfs1(int x,int f){
	sz[x]=1;
	fa[x]=f;
	deep[x]=deep[f]+1;
	for(int i=0;i<(int)E[x].size();i++){
		int y=E[x][i];
		if(y!=f){
			dfs1(y,x);
			sz[x]+=sz[y];
			if(sz[y]>sz[son[x]]) son[x]=y;
		}
	}
}
void dfs2(int x,int t){
	dfn[x]=++tim;
	top[x]=t;
	if(son[x]) dfs2(son[x],t);
	for(int i=0;i<(int)E[x].size();i++){
		int y=E[x][i];
		if(y!=fa[x]&&y!=son[x]) dfs2(y,y); 
	}
}
int lca(int x,int y){
	while(top[x]!=top[y]){
		if(deep[top[x]]<deep[top[y]]) swap(x,y);
		x=fa[top[x]];
	}
	return deep[x]<deep[y]?x:y;
}

vector<int>vt[maxm+5];//虚树
bool is_orig[maxm+5];//虚树中的关键点(不含lca) 
int stk[maxm+5],_top; 
inline int cmp(int x,int y){
	return dfn[x]<dfn[y];
}
void vt_insert(int x){
	if(_top<1){
		stk[++_top]=x;
		return;
	}
	int lc=lca(x,stk[_top]);
	if(stk[_top]==lc){
		stk[++_top]=x;
		return;
	}
	while(_top>1&&deep[stk[_top-1]]>=deep[lc]){
		vt[stk[_top-1]].push_back(stk[_top]);
		_top--;
	}
	if(stk[_top]!=lc) vt[lc].push_back(stk[_top]);
	stk[_top]=lc;
	stk[++_top]=x;
} 
long long ans=0;
int dfs3(int x,int f){
	int szx=0;//子树中关键点个数,只开一个变量,这样不用清空
	if(is_orig[x]) szx++; 
	for(int i=0;i<(int)vt[x].size();i++){
		int y=vt[x][i];
		if(y!=f){
			int szy=dfs3(y,x);
			ans=(ans+1ll*szx*szy*S.t[x].len%mod)%mod;
			szx+=szy;
		}
		
	}
	vt[x].clear(); 
	return szx;
}
void get_tree(int *p,int cnt){
	sort(p+1,p+1+cnt,cmp);
	cnt=unique(p+1,p+1+cnt)-p-1;//题目说可能有重复后缀 
	_top=0;
	for(int i=1;i<=cnt;i++) is_orig[p[i]]=1; 
	int root=0;
	for(int i=1;i<=cnt;i++){
		if(!root) root=p[i];
		else root=lca(root,p[i]);
	}
	for(int i=1;i<=cnt;i++) vt_insert(p[i]);
	while(_top>1){
		vt[stk[_top-1]].push_back(stk[_top]);
		_top--;
	}
	ans=0;
	dfs3(root,0);
	for(int i=1;i<=cnt;i++) is_orig[p[i]]=0; 
}
int main(){
	qread(n);
	qread(m);
	scanf("%s",str+1);
	S.build(str);
	dfs1(1,0);
	dfs2(1,1); 
	for(int i=1;i<=m;i++){
		qread(qu);
		for(int j=1;j<=qu;j++){
			qread(q[j]);
			q[j]=pos[q[j]];
		}
		get_tree(q,qu);
		qprint(ans);
		putchar('
');
	}
}
/*
4 3
aaaa
2 3 4
*/ 
原文地址:https://www.cnblogs.com/birchtree/p/12594319.html