日更两篇,真刺激。
题目
题意:给出一个串,若干个询问。每个询问给出一些后缀,问它们两两之间LCP的长度和。
后缀数组的写法太简单显然了,把后缀按顺序拿出来,只用一个单调栈就可以了,但我并不会…所以就是SAM了。
经过一轮求后缀LCP模型在SAM上乱套,发现就是反串建SAM,得到后缀树。后缀树上结点的LCA的长度就是LCP长度。对于这里,把询问给出的结点在后缀树上建出虚树,每个点算贡献即可。
具体是这样的,对于x的两个儿子son1和son2,
siz[son1]∗siz[son2]∗dep[x]
就能贡献答案。
#include <iostream>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
#define mmst(a, b) memset(a, b, sizeof(a))
#define mmcp(a, b) memcpy(a, b, sizeof(b))
typedef long long LL;
const int N=1001000;
const LL mo=23333333333333333;
void read(int &hy)
{
hy=0;
char cc=getchar();
while(cc<'0'||cc>'9')
cc=getchar();
while(cc>='0'&&cc<='9')
{
hy=(hy<<3)+(hy<<1)+cc-'0';
cc=getchar();
}
}
int n,m,k,a[N];
int son[N][26],pre[N],dep[N],w[N],cnt=1,last=1;
int head[N],nex[N],to[N],cur;
int Chtholly[N],top[N],len[N],tim[N],siz[N],times;
int st[N];
char cc[N];
LL ans;
bool vis[N];
void Insert(int x,int num)
{
dep[++cnt]=dep[last]+1;
w[num]=cnt;
int np=cnt,p=last;
last=cnt;
for(;!son[p][x];p=pre[p])
son[p][x]=np;
if(!p)
pre[np]=1;
else
{
int q=son[p][x];
if(dep[q]==dep[p]+1)
pre[np]=q;
else
{
dep[++cnt]=dep[p]+1;
int nq=cnt;
pre[nq]=pre[q];
pre[q]=pre[np]=nq;
mmcp(son[nq],son[q]);
for(;son[p][x]==q;p=pre[p])
son[p][x]=nq;
}
}
}
void add(int u,int v)
{
to[++cur]=v;
nex[cur]=head[u];
head[u]=cur;
}
void dfs(int x)
{
siz[x]=1;
tim[x]=++times;
for(int h=head[x];h;h=nex[h])
{
len[to[h]]=len[x]+1;
dfs(to[h]);
siz[x]+=siz[to[h]];
if(siz[to[h]]>siz[Chtholly[x]])
Chtholly[x]=to[h];
}
}
void dfs2(int x,int tp)
{
top[x]=tp;
if(Chtholly[x])
dfs2(Chtholly[x],tp);
for(int h=head[x];h;h=nex[h])
if(to[h]!=Chtholly[x])
dfs2(to[h],to[h]);
}
int lca(int x,int y)
{
while(top[x]!=top[y])
{
if(len[top[x]]<len[top[y]])
swap(x,y);
x=pre[top[x]];
}
return len[x]<len[y] ? x : y;
}
bool cmp(int x,int y)
{
return tim[x]<tim[y];
}
void dp(int x)
{
siz[x]=vis[x] ? 1 : 0;
for(int h=head[x];h;h=nex[h])
{
dp(to[h]);
ans+=(LL)siz[to[h]]*siz[x]*dep[x];
siz[x]+=siz[to[h]];
}
head[x]=0;
}
void work()
{
ans=0;
k=cur=0;
int kk;
read(kk);
for(int i=1;i<=kk;i++)
{
int by;
read(by);
if(!vis[w[by]])
a[++k]=w[by];
vis[w[by]]=1;
}
sort(a+1,a+k+1,cmp);
int t=0;
for(int i=1;i<=k;i++)
{
if(!t)
{
st[++t]=a[i];
continue;
}
int lc=lca(a[i],st[t]);
while(lc!=st[t])
{
if(tim[lc]>=tim[st[t-1]])
{
add(lc,st[t--]);
if(lc!=st[t])
st[++t]=lc;
break;
}
else
add(st[t-1],st[t]),t--;
}
st[++t]=a[i];
}
while(t!=1)
add(st[t-1],st[t]),t--;
dp(st[1]);
for(int i=1;i<=k;i++)
vis[a[i]]=0;
printf("%lld\n",ans);
}
int main()
{
cin>>n>>m;
scanf("%s",cc);
for(int i=n;i>=1;i--)
Insert(cc[i-1]-'a',i);
for(int i=2;i<=cnt;i++)
add(pre[i],i);
dfs(1);
dfs2(1,1);
mmst(head,0);
while(m--)
work();
return 0;
}