题意
有一个长度为n的仅包含小写字母的字符串S,下标范围为[1,n].
现在有若干组询问,对于每一个询问,我们给出若干个后缀(以其在S中出现的起始位置来表示),求这些后缀两两之间的LCP(LongestCommonPrefix)的长度之和.一对后缀之间的LCP长度仅统计一遍.
有S<=5*10^5,且Σt<=3*10^6.
分析
我们把S反过来后建后缀自动机,就转换成了求两两前缀的lcs。显然两个前缀的lcs等于其对应位置在parents树上lca的mx。
那么我们可以把这几个节点的虚树建出来,然后树形dp一下即可
Code
#include <bits/stdc++.h>
typedef long long ll;
const int N = 1000005;
int read()
{
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
return x * f;
}
int ch[N][26];
int num[N],val[N],fa[N];
int sz,last;
void ins(int id,int x)
{
int p,q,np,nq;
p = last;
num[id] = last = np = ++sz;
val[np] = val[p] + 1;
for (; p && !ch[p][x]; p = fa[p])
ch[p][x] = np;
if (!p)
fa[np] = 1;
else
{
q = ch[p][x];
if (val[q] == val[p] + 1)
fa[np] = q;
else
{
nq = ++sz;
val[nq] = val[p] + 1;
memcpy(ch[nq], ch[q], sizeof(ch[q]));
fa[nq] = fa[q];
fa[q] = fa[np] = nq;
for (; ch[p][x] == q; p = fa[p])
ch[p][x] = nq;
}
}
}
struct Edge
{
int to,next;
}e[N * 5];
int next1[N], next2[N];
int cnt;
void add1(int x,int y)
{
e[++cnt].to = y, e[cnt].next = next1[x], next1[x] = cnt;
}
void add2(int x,int y)
{
e[++cnt].to = y, e[cnt].next = next2[x], next2[x] = cnt;
}
int bin[25];
int dfn[N],arr[N],dep[N];
int rmq[N * 2][25],lg[25];
int timDfn,tim;
bool cmp(int x,int y)
{
return dfn[x] < dfn[y];
}
void getRmq()
{
for (int i = 1; i <= tim; i++)
lg[i] = log(i) / log(2);
for (int j = 1; j <= lg[tim]; j++)
for (int i = 1; i + bin[j] - 1 <= tim; i++)
rmq[i][j] = dep[rmq[i][j - 1]] < dep[rmq[i + bin[j - 1]][j - 1]] ? rmq[i][j - 1] : rmq[i + bin[j - 1]][j - 1];
}
void dfs(int x)
{
dfn[x] = ++timDfn;
dep[x] = dep[fa[x]] + 1;
rmq[++tim][0] = x;
arr[x] = tim;
for (int i = next1[x]; i; i = e[i].next)
{
if (e[i].to == fa[x])
continue;
dfs(e[i].to);
rmq[++tim][0] = x;
}
}
int getLca(int x,int y)
{
int l = std::min(arr[x], arr[y]), r = std::max(arr[x], arr[y]);
int len = lg[r - l + 1];
return dep[rmq[l][len]] < dep[rmq[r - bin[len] + 1][len]] ? rmq[l][len] : rmq[r - bin[len] + 1][len];
}
ll ans;
int size[N];
void dp(int x)
{
for (int i = next2[x]; i; i = e[i].next)
{
dp(e[i].to);
ans += 1ll * val[x] * size[x] * size[e[i].to];
size[x] += size[e[i].to];
size[e[i].to] = 0;
next2[e[i].to] = 0;
}
}
int stack[N];
int tot;
int a[N];
ll solve()
{
int top = 1, tmp = cnt;
stack[top] = 1;
for (int i = 1; i <= tot; i++)
{
int x = a[i], lca = getLca(x, stack[top]);
size[x] = 1;
while (top > 1 && dep[stack[top - 1]] > dep[lca])
add2(stack[top - 1], stack[top]), top--;
if (dep[stack[top]] > dep[lca])
add2(lca, stack[top]), top--;
if (lca != stack[top])
stack[++top] = lca;
stack[++top] = x;
}
while (top > 1)
add2(stack[top - 1], stack[top]), top--;
ans = 0;
dp(1);
size[1] = 0;
next2[1] = 0;
cnt = tmp;
return ans;
}
char str[N];
int main()
{
bin[0] = 1;
for (int i = 1; i <= 20; i++)
bin[i] = bin[i - 1] * 2;
int n = read();
int m = read();
scanf("%s", str + 1);
for (int i = 1; i * 2 <= n; i++)
std::swap(str[i], str[n - i + 1]);
last = sz = 1;
for (int i = 1; i <= n; i++)
ins(i, str[i] - 'a');
for (int i = 2; i <= sz; i++)
add1(fa[i],i);
dfs(1);
getRmq();
while (m--)
{
tot = read();
for (int i = 1; i <= tot; i++)
a[i] = num[n - read() + 1];
std::sort(a + 1, a + tot + 1, cmp);
tot = std::unique(a + 1, a + tot + 1) - a - 1;
printf("%lld\n",solve());
}
}