题意:
题目翻译很清楚,告辞。
sol:
我们考虑到 (lcp(x,y)) 等价于反串SAM上的 (dep_{lca(x,y)})。
然后我们对 给出串的反串 建一个后缀自动机,然后记一下 (pos_x) 是 (x) 这个位置在后缀自动机上的节点位置。
怎么求 (sum_{ain A} sum_{b in B} lca(a,b)) 。
考虑给出的集合 (sum A,sum B leq 4 imes10^5)。
我们直接虚树就好了吧。如果你做过品酒大会,你应该会下面这个套路。
// powered by c++11
// by Isaunoya
#include <bits/stdc++.h>
#define rep(i, x, y) for (register int i = (x); i <= (y); ++i)
#define Rep(i, x, y) for (register int i = (x); i >= (y); --i)
using namespace std;
using db = double;
using ll = long long;
using uint = unsigned int;
using ull = unsigned long long;
using pii = pair<int, int>;
#define Tp template
#define fir first
#define sec second
Tp<class T> void cmax(T& x, const T& y) {
if (x < y) x = y;
}
Tp<class T> void cmin(T& x, const T& y) {
if (x > y) x = y;
}
#define all(v) v.begin(), v.end()
#define sz(v) ((int)v.size())
#define pb emplace_back
Tp<class T> void sort(vector<T>& v) { sort(all(v)); }
Tp<class T> void reverse(vector<T>& v) { reverse(all(v)); }
Tp<class T> void unique(vector<T>& v) { sort(all(v)), v.erase(unique(all(v)), v.end()); }
inline void reverse(string& s) { reverse(s.begin(), s.end()); }
const int SZ = 1 << 23 | 233;
struct FILEIN {
char qwq[SZ], *S = qwq, *T = qwq, ch;
#ifdef __WIN64
#define GETC getchar
#else
inline char GETC() { return (S == T) && (T = (S = qwq) + fread(qwq, 1, SZ, stdin), S == T) ? EOF : *S++; }
#endif
inline FILEIN& operator>>(char& c) {
while (isspace(c = GETC()))
;
return *this;
}
inline FILEIN& operator>>(string& s) {
s.clear();
while (isspace(ch = GETC()))
;
if (!~ch) return *this;
s = ch;
while (!isspace(ch = GETC()) && ~ch) s += ch;
return *this;
}
inline FILEIN& operator>>(char* str) {
char* cur = str;
while (*cur) *cur++ = 0;
cur = str;
while (isspace(ch = GETC()))
;
if (!~ch) return *this;
*cur = ch;
while (!isspace(ch = GETC()) && ~ch) *++cur = ch;
*++cur = 0;
return *this;
}
Tp<class T> inline void read(T& x) {
bool f = 0;
while ((ch = GETC()) < 48 && ~ch) f ^= (ch == 45);
x = ~ch ? (ch ^ 48) : 0;
while ((ch = GETC()) > 47) x = x * 10 + (ch ^ 48);
x = f ? -x : x;
}
inline FILEIN& operator>>(int& x) { return read(x), *this; }
inline FILEIN& operator>>(ll& x) { return read(x), *this; }
inline FILEIN& operator>>(uint& x) { return read(x), *this; }
inline FILEIN& operator>>(ull& x) { return read(x), *this; }
inline FILEIN& operator>>(double& x) {
read(x);
bool f = x < 0;
x = f ? -x : x;
if (ch ^ '.') return *this;
double d = 0.1;
while ((ch = GETC()) > 47) x += d * (ch ^ 48), d *= .1;
return x = f ? -x : x, *this;
}
} in;
struct FILEOUT {
const static int LIMIT = 1 << 22;
char quq[SZ], ST[233];
int sz, O, pw[233];
FILEOUT() {
set(7);
rep(i, pw[0] = 1, 9) pw[i] = pw[i - 1] * 10;
}
~FILEOUT() { flush(); }
inline void flush() { fwrite(quq, 1, O, stdout), fflush(stdout), O = 0; }
inline FILEOUT& operator<<(char c) { return quq[O++] = c, *this; }
inline FILEOUT& operator<<(string str) {
if (O > LIMIT) flush();
for (char c : str) quq[O++] = c;
return *this;
}
inline FILEOUT& operator<<(char* str) {
if (O > LIMIT) flush();
char* cur = str;
while (*cur) quq[O++] = (*cur++);
return *this;
}
Tp<class T> void write(T x) {
if (O > LIMIT) flush();
if (x < 0) {
quq[O++] = 45;
x = -x;
}
do {
ST[++sz] = x % 10 ^ 48;
x /= 10;
} while (x);
while (sz) quq[O++] = ST[sz--];
}
inline FILEOUT& operator<<(int x) { return write(x), *this; }
inline FILEOUT& operator<<(ll x) { return write(x), *this; }
inline FILEOUT& operator<<(uint x) { return write(x), *this; }
inline FILEOUT& operator<<(ull x) { return write(x), *this; }
int len, lft, rig;
void set(int l) { len = l; }
inline FILEOUT& operator<<(double x) {
bool f = x < 0;
x = f ? -x : x, lft = x, rig = 1. * (x - lft) * pw[len];
return write(f ? -lft : lft), quq[O++] = '.', write(rig), *this;
}
} out;
#define int long long
struct Math {
vector<int> fac, inv;
int mod;
void set(int n, int Mod) {
fac.resize(n + 1), inv.resize(n + 1), mod = Mod;
rep(i, fac[0] = 1, n) fac[i] = fac[i - 1] * i % mod;
inv[n] = qpow(fac[n], mod - 2);
Rep(i, n - 1, 0) inv[i] = inv[i + 1] * (i + 1) % mod;
}
int qpow(int x, int y) {
int ans = 1;
for (; y; y >>= 1, x = x * x % mod)
if (y & 1) ans = ans * x % mod;
return ans;
}
int C(int n, int m) {
if (n < 0 || m < 0 || n < m) return 0;
return fac[n] * inv[m] % mod * inv[n - m] % mod;
}
int gcd(int x, int y) { return !y ? x : gcd(y, x % y); }
int lcm(int x, int y) { return x * y / gcd(x, y); }
} math;
int n;
const int maxn = 4e5 + 54;
vector<int> g[maxn];
struct suffixautomaton {
int las, cnt;
suffixautomaton() { las = cnt = 1; }
int ch[maxn][26], fa[maxn], len[maxn], pos[maxn];
void ins(int c, int id) {
int p = las, np = las = ++cnt;
len[np] = len[p] + 1, pos[id] = np;
for (; p && !ch[p][c]; p = fa[p]) ch[p][c] = np;
if (!p) {
fa[np] = 1;
} else {
int q = ch[p][c];
if (len[q] == len[p] + 1) {
fa[np] = q;
} else {
int nq = ++cnt;
memcpy(ch[nq], ch[q], sizeof(ch[q]));
len[nq] = len[p] + 1, fa[nq] = fa[q], fa[q] = fa[np] = nq;
for (; p && ch[p][c] == q; p = fa[p]) ch[p][c] = nq;
}
}
}
void ins(char* S) {
char* Cur = S;
int qwq = 0;
while (*Cur) ins((*Cur++) - 'a', ++qwq);
}
void build() { rep(i, 2, cnt) g[fa[i]].pb(i); }
} sam;
int dfn[maxn], ed[maxn], idx = 0;
struct LCA {
int f[maxn][22], dep[maxn];
void dfs(int u) {
dfn[u] = ++idx;
for (int v : g[u]) dep[v] = dep[u] + 1, f[v][0] = u, dfs(v);
ed[u] = ++idx;
}
void solve() {
dfs(1);
rep(j, 1, 20) rep(i, 1, sam.cnt) f[i][j] = f[f[i][j - 1]][j - 1];
}
int lca(int x, int y) {
if (dep[x] < dep[y]) x ^= y ^= x ^= y;
for (int i = 20; ~i; i--)
if (dep[f[x][i]] >= dep[y]) x = f[x][i];
if (x == y) return x;
for (int i = 20; ~i; i--)
if (f[x][i] ^ f[y][i]) {
x = f[x][i], y = f[y][i];
}
return f[x][0];
}
} Lca;
// lcp = rev sam
char s[maxn];
struct Tree {
int dp[maxn][2], op[maxn << 1], tot = 0, st[maxn], top = 0;
bool book[maxn];
int solve(int k1, int k2) {
tot = 0;
rep(i, 1, k1) {
int x;
in >> x, x = n - x + 1, x = sam.pos[x], dp[x][0] = 1;
if (!book[x]) op[++tot] = x, book[x] = 1;
}
rep(i, 1, k2) {
int x;
in >> x, x = n - x + 1, x = sam.pos[x], dp[x][1] = 1;
if (!book[x]) op[++tot] = x, book[x] = 1;
}
sort(op + 1, op + tot + 1, [](int x, int y) { return dfn[x] < dfn[y]; });
for (int i = 1, lim = tot; i < lim; ++i) {
int lca = Lca.lca(op[i], op[i + 1]);
if (!book[lca]) op[++tot] = lca, book[lca] = 1;
}
rep(i, 1, tot) book[op[i]] = false;
for (int i = 1, lim = tot; i <= lim; ++i) op[++tot] = -op[i];
sort(op + 1, op + tot + 1,
[](int x, int y) { return (x > 0 ? dfn[x] : ed[-x]) < (y > 0 ? dfn[y] : ed[-y]); });
int ans = 0;
rep(i, 1, tot) {
if (op[i] > 0) {
int u = op[i];
st[++top] = u, ans += sam.len[u] * dp[u][0] * dp[u][1];
} else {
if (top == 1) {
dp[st[top]][0] = dp[st[top]][1] = 0, top--;
} else {
int u = st[top--], f = st[top];
ans += sam.len[f] * (dp[f][0] * dp[u][1] + dp[f][1] * dp[u][0]);
dp[f][0] += dp[u][0], dp[f][1] += dp[u][1], dp[u][0] = dp[u][1] = 0;
}
}
}
return ans;
}
} tree;
signed main() {
// code begin.
int _;
in >> n >> _ >> s;
reverse(s, s + n), sam.ins(s), sam.build(), Lca.solve();
while (_--) {
int k1, k2;
in >> k1 >> k2;
out << tree.solve(k1, k2) << '
';
}
return 0;
// code end.
}