BZOJ-3879: SvT
后缀树+虚树
做这道题我们需要知道这么几个东西
-
两个后缀的\(lcp\)就是其后缀树上的\(lca\)
-
用一个字符串的反串建\(sam\),其\(parent\)边就构成了原串的后缀树
这道题要求询问\(k\)个后缀两两之间\(lcs\)之和,转化到后缀树中就是问这\(k\)个点任意两个点\(lca\)处值相加
考虑树形\(DP\),计算每个点是多少对点的\(LCA\),然而这样时间复杂度一次\(O(n)\),总共\(O(m*n)\)
对于每一次询问的\(k\)个点,在后缀树上建虚树,时间复杂度就是\(O(\sum t)\)啦
#include<map>
#include<queue>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=1e6+100,maxm=1e6+100;
int n,t,m,a[maxn],fa[maxn],dep[maxn],son[maxn],siz[maxn],top[maxn],bj[maxn];
int nex[maxm],v[maxm],head[maxn],num=1,dfn[maxn],tim,tp,st[maxn],bh[maxn];
char s[maxn];
vector<int>e[maxn];
void add(int x,int y){
v[++num]=y;
nex[num]=head[x];
head[x]=num;
}
struct SAM{
int son[maxn][26],len[maxn],fa[maxn];
int last,tot;
SAM(){last=tot=1;}
void insert(int x){
int p=last,np=++tot;
len[np]=len[p]+1;
while(p&&son[p][x]==0)
son[p][x]=np,p=fa[p];
if(!p)
fa[np]=1;
else{
int q=son[p][x];
if(len[q]==len[p]+1)
fa[np]=q;
else{
int nq=++tot;
memcpy(son[nq],son[q],sizeof(son[q]));
fa[nq]=fa[q];
len[nq]=len[p]+1;
fa[q]=fa[np]=nq;
while(p&&son[p][x]==q)
son[p][x]=nq,p=fa[p];
}
}
last=np;
}
void work(){
for(int i=2;i<=tot;i++)
add(fa[i],i);
}
}sam;
void dfs1(int x,int f,int d){
fa[x]=f;
dep[x]=d;
siz[x]=1;
dfn[x]=++tim;
for(int i=head[x];i;i=nex[i])
if(v[i]!=f){
dfs1(v[i],x,d+1);
if(siz[son[x]]<siz[v[i]])
son[x]=v[i];
siz[x]+=siz[v[i]];
}
}
void dfs2(int x,int tp){
top[x]=tp;
if(son[x]) dfs2(son[x],tp);
for(int i=head[x];i;i=nex[i])
if(v[i]!=fa[x]&&v[i]!=son[x])
dfs2(v[i],v[i]);
}
int Lca(int x,int y){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])
swap(x,y);
x=fa[top[x]];
}
return dep[x]>dep[y]?y:x;
}
bool cmp(int x,int y){
return dfn[x]<dfn[y];
}
void insert(int x){
if(!tp){
st[++tp]=x;
return;
}
int lca=Lca(x,st[tp]);
while(tp>1&&dfn[st[tp-1]]>=dfn[lca])
e[st[tp-1]].push_back(st[tp]),tp--;
if(st[tp]!=lca) e[lca].push_back(st[tp]),st[tp]=lca;
st[++tp]=x;
}
ll dp(int x,ll &z){
ll tmp=0,tot=bj[x];
for(int i=0;i<e[x].size();i++){
int y=e[x][i];
tmp=dp(y,z);
z+=tot*tmp*sam.len[x];
tot+=tmp;
}
bj[x]=0;
e[x].clear();
return tot;
}
void vtree(){
scanf("%d",&m);
for(int i=1;i<=m;i++)
scanf("%d",&a[i]),a[i]=bh[a[i]],bj[a[i]]=1;
sort(a+1,a+m+1,cmp);
st[tp=1]=1;
a[0]=-1;
for(int i=1;i<=m;i++) if(a[i]!=a[i-1]) insert(a[i]);
while(tp>1) e[st[tp-1]].push_back(st[tp]),tp--;tp--;
ll ans=0;
dp(1,ans);
printf("%lld\n",ans);
}
int main(){
scanf("%d%d",&n,&t);
scanf("%s",s+1);
for(int i=n;i>=1;i--) sam.insert(s[i]-'a'),bh[i]=sam.last;
sam.work();
dfs1(1,1,1);
dfs2(1,1);
while(t--)
vtree();
return 0;
}