蛮好的一道fail树的题目
考虑fail树,对于询问l,r,k,相当于询问l~r的字符串在AC自动机上对应节点在fail树子树中是第k个串前缀的节点的个数和。
发现k固定有一种O(n+Σ串长)的优秀做法,就不说了。同时对一些询问存在使用分块数据结构维护的离线O(Σ询问串长)的做法,就可以分块了,复杂度O(nsort(n))
这个离线O(Σ询问串长)做法把询问挂在fail树上,dfs的同时维护到根节点的字符串中点节点,遇到串的前缀即询问,就更新答案,由于这部分处理的串长均小于sqrt(n)所以询问节点共nsort(n),使用O(sqrt(n))修改,O(1)查询分块,达到O(nsqrt(n))。
UPD:估计没人能看懂这样的题解,有时间再写一份(flag
#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
#define N 100005
#define ll long long
using namespace std;
int n,l[N],r[N],bl[N],SZ=1000,R[N],L[N],K[N],cnt,sz=1000,Q,sum;
int nod,v[N][26],fail[N],q[N],fa[N],ss[N],ly[N];ll vv[N],val[N],ans[N];
char s[N];
vector<int>a[N],b[N],c[N],d[N];
void mdy(int x,int y)
{
for (int i=bl[x]+1;i<=sum;i++)
val[i]+=y;
for (int i=x;i<=bl[x]*SZ;i++)
vv[i]+=y;
}
ll qry(int x)
{
if (x==0) return 0;
return val[bl[x]]+vv[x];
}
void dfs(int x)
{
for (int i=0;i<a[x].size();i++)
mdy(a[x][i],1);
for (int i=0,t;i<b[x].size();i++)
{
for (int j=0;j<d[b[x][i]].size();j++)
{
t=d[b[x][i]][j];
ans[t]+=qry(R[t])-qry(L[t]-1);
}
}
for (int i=0;i<c[x].size();i++)
dfs(c[x][i]);
for (int i=0;i<a[x].size();i++)
mdy(a[x][i],-1);
}
void build()
{
for (int i=0;i<26;i++)
v[0][i]=1;
int l=0,r=1;
q[1]=1;
while(l<r)
{
int x=q[++l];
if (x==1) fail[1]=0;
else fail[x]=v[fail[fa[x]]][ss[x]],c[fail[x]].push_back(x);
for (int i=0;i<26;i++)
if (v[x][i]) q[++r]=v[x][i];
else v[x][i]=v[fail[x]][i];
}
}
void Dfs(int x)
{
for (int i=0;i<c[x].size();i++)
{
Dfs(c[x][i]);
ly[x]+=ly[c[x][i]];
}
for (int i=0;i<a[x].size();i++)
val[a[x][i]]+=ly[x];
}
//a:终止节点
//b:fail节点
//c:son节点
//d:update节点
int main()
{
scanf("%d%d",&n,&Q);
nod=1;
for (int i=1;i<=n;i++)
{
l[i]=cnt+1;
char c=getchar();
while(c<'a'||c>'z')c=getchar();
while(c>='a'&&c<='z')s[++cnt]=c-'a',c=getchar();
r[i]=cnt;
int now=1;
for (int j=l[i];j<=r[i];j++)
{
if (!v[now][s[j]]) v[now][s[j]]=++nod,fa[nod]=now,ss[nod]=s[j];
now=v[now][s[j]];
if (r[i]-l[i]+1<=sz) b[now].push_back(i);
}
a[now].push_back(i);
}
build();
for (int i=1;i<=n;i++)
bl[i]=(i+SZ-1)/SZ;
sum=bl[n];
for (int i=1;i<=Q;i++)
{
scanf("%d%d%d",&L[i],&R[i],&K[i]);
d[K[i]].push_back(i);
}
dfs(1);
for (int i=1;i<=n;i++)
if (r[i]-l[i]+1>sz)
{
memset(ly,0,sizeof ly);
memset(val,0,sizeof val);
int now=1;
for (int j=l[i];j<=r[i];j++)
{
now=v[now][s[j]];
ly[now]++;
}
Dfs(1);
for (int j=1;j<=n;j++)
val[j]+=val[j-1];
for (int j=0;j<d[i].size();j++)
ans[d[i][j]]=val[R[d[i][j]]]-val[L[d[i][j]]-1];
}
for (int i=1;i<=Q;i++)
printf("%lld\n",ans[i]);
}