SvT[BZOJ3879]
【题目描述】
有一个长度为n的仅包含小写字母的字符串\(S\),下标范围为\([1,n]\).
现在有若干组询问,对于每一个询问,我们给出若干个后缀(以其在\(S\)中出现的起始位置来表示),求这些后缀两两之间的LCP(LongestCommonPrefix)的长度之和.一对后缀之间的LCP长度仅统计一遍.
【输入格式】
第一行两个正整数\(n,m\),分别表示\(S\)的长度以及询问的次数.
接下来一行有一个字符串\(S\).
接下来有\(m\)组询问,对于每一组询问,均按照以下格式在一行内给出:
首先是一个整数\(t\),表示共有多少个后缀.接下来\(t\)个整数分别表示\(t\)个后缀在字符串\(S\)中的出现位置.
【输出格式】
对于每一组询问,输出一行一个整数,表示该组询问的答案.由于答案可能很大,仅需要输出这个答案对于\(23333333333333333\)(一个巨大的质数)取模的余数
题解
这不是和 差异「AHOI2013」 几乎一模一样吗。。。那题就相当于询问是\(1\sim n\)的所有后缀 然后式子稍微变变形
这题也是一样 先建好后缀数组 对于每组询问 先按照后缀数组的\(rnk\)进行排序 然后去重 然后用ST表来求出排序后相邻两个后缀的LCP
剩下的单调栈DP就跟上面那题一样了 不想再敲一遍了 请点上面传送门 看解法一:后缀数组
唯一的区别就是多个ST表。。。
#include <bits/stdc++.h>
#define N 500005
using namespace std;
typedef long long ll;
const ll mod = 23333333333333333;
int n, m, t;
char s[500005];
int sa[N], sum[N], rnk[N], sa2[N], key[N], height[N], q[N], tmp[N], stk[N], top;
ll f[N], ans;
inline bool check(int *num, int a, int b, int l) {
return num[a] == num[b] && num[a+l] == num[b+l];
}
inline void DA(int _m) {
int i, j, p;
for (i = 1; i <= _m; i++) sum[i] = 0;
for (i = 1; i <= n; i++) sum[rnk[i]=s[i]]++;
for (i = 2; i <= _m; i++) sum[i] += sum[i-1];
for (i = n; i >= 1; i--) sa[sum[rnk[i]]--] = i;
for (j = 1, p = 0; j <= n; j <<= 1, _m = p) {
p = 0; for (i = n - j + 1; i <= n; i++) sa2[++p] = i;
for (i = 1; i <= n; i++) if (sa[i] > j) sa2[++p] = sa[i] - j;
for (i = 1; i <= n; i++) key[i] = rnk[sa2[i]];
for (i = 1; i <= _m; i++) sum[i] = 0;
for (i = 1; i <= n; i++) sum[key[i]]++;
for (i = 2; i <= _m; i++) sum[i] += sum[i-1];
for (i = n; i >= 1; i--) sa[sum[key[i]]--] = sa2[i];
for (swap(rnk, sa2), p = 2, rnk[sa[1]] = 1, i = 2; i <= n; i++) {
rnk[sa[i]] = check(sa2, sa[i-1], sa[i], j) ? p - 1 : p++;
}
}
}
inline void geth() {
ll p = 0;
for (int i = 1; i <= n; i++) rnk[sa[i]] = i;
for (int i = 1; i <= n; i++) {
if (p) p--;
int j = sa[rnk[i]-1];
while (s[i + p] == s[j + p]) p++;
height[rnk[i]] = p;
}
}
int st[N][21];
void preST() {
for (int i = 1; i <= n; i++) st[i][0] = height[i];
for (int l = 1; (1 << l) <= n; l++) {
for (int i = 1; i <= n - (1 << l) + 1; i++) {
st[i][l] = min(st[i][l-1], st[i+(1<<(l-1))][l-1]);
}
}
}
inline int query(int x, int y) {
int l = log2(y - x + 1);
return min(st[x][l], st[y - (1 << l) + 1][l]);
}
inline bool cmp(int x, int y) {
return rnk[x] < rnk[y];
}
int main() {
scanf("%d %d %s", &n, &m, s + 1);
DA(128);
geth();
preST();
for (int i = 1; i <= m; i++) {
scanf("%d", &t);
ans = 0;
for (int j = 1; j <= t; j++) scanf("%d", &q[j]);
sort(q + 1, q + t + 1, cmp);
t = unique(q + 1, q + t + 1) - q - 1;
for (int j = 2; j <= t; j++) {
tmp[j] = query(rnk[q[j-1]] + 1, rnk[q[j]]);
}
stk[top=1] = 1;
for (int j = 2; j <= t; j++) {
while (top && tmp[stk[top]] > tmp[j]) top--;
f[j] = f[stk[top]] + (j - stk[top]) * tmp[j];
stk[++top] = j;
ans += f[j];
}
printf("%lld\n", ans);
}
return 0;
}