题意:
给定一个字符串 s s s, q q q 次询问,每次给定 k i , l i k_i, l_i ki,li,以及 k i k_i ki 个数 a j a_j aj、 l i l_i li 个数 b k b_k bk,求 ∑ j = 1 k i ∑ k = 1 l i L C P ( s [ a j ⋯ , n ] , s [ b k ⋯ , n ] ) \sum\limits_{j = 1}^{k_i}\sum\limits_{k = 1}^{l_i} LCP(s[a_j \cdots,n],~s[b_k \cdots, n]) j=1∑kik=1∑liLCP(s[aj⋯,n], s[bk⋯,n])。 ( n , q , ∑ k i , ∑ l i ≤ 2 × 1 0 5 ) (n, q, \sum k_i, \sum l_i \leq 2×10^5) (n,q,∑ki,∑li≤2×105)
链接:
https://codeforces.com/problemset/problem/1073/G
解题思路:
先考虑单次询问如何求解。对串 s s s 的反串建立后缀自动机,则求 l c p lcp lcp 变为求最长公共后缀,答案为两个前缀在自动机上的 l c a lca lca 对应结点的 l e n len len,那么通过枚举 l c a lca lca,设为结点 u u u,则 u u u 贡献的公共后缀长度为 l e n [ u ] − l e n [ p a r [ u ] ] len[u] - len[par[u]] len[u]−len[par[u]],对数就是其子树内 a , b a,b a,b 对应前缀的个数之积。
这样求解单次为 O ( n ) O(n) O(n),但实际上用到的点不多,可以建立虚树,在虚树上 d p dp dp 统计。
解题思路:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define sz(a) ((int)a.size())
#define pb push_back
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
const int maxn = 4e5 + 5;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
char s[maxn];
vector<int> G[maxn], P[maxn], pi;
int nxt[maxn][26], par[maxn], len[maxn], pos[maxn];
int dfn[maxn], siz[maxn], son[maxn], dep[maxn], fa[maxn], top[maxn];
int num[maxn][2], stk[maxn], wi[maxn];
int n, m, cnt, last, tim; ll ans;
int add(int l){
++cnt, len[cnt] = l; return cnt;
}
void init(){
cnt = 0, last = add(0);
}
void insert(char ch){
int t = ch - 'a', p = last, cur;
cur = last = add(len[p] + 1);
while(p && !nxt[p][t]) nxt[p][t] = cur, p = par[p];
if(!p) { par[cur] = 1; return; }
int q = nxt[p][t];
if(len[q] == len[p] + 1) { par[cur] = q; return; }
int nq = add(len[p] + 1);
memcpy(nxt[nq], nxt[q], sizeof nxt[q]);
par[nq] = par[q], par[q] = par[cur] = nq;
while(p && nxt[p][t] == q) nxt[p][t] = nq, p = par[p];
}
void dfs1(int u, int f){
dep[u] = dep[f] + 1, siz[u] = 1, son[u] = 0, fa[u] = f;
for(auto &v : G[u]){
dfs1(v, u);
siz[u] += siz[v];
son[u] = siz[v] > siz[son[u]] ? v : son[u];
}
}
void dfs2(int u, int t){
dfn[u] = ++tim, top[u] = t;
if(!son[u]) return;
dfs2(son[u], t);
for(auto &v : G[u]){
if(v == son[u] || v == fa[u]) continue;
dfs2(v, v);
}
}
int lca(int u, int v){
while(top[u] != top[v]){
dep[top[u]] > dep[top[v]] ? u = fa[top[u]] : v = fa[top[v]];
}
return dep[u] < dep[v] ? u : v;
}
void link(int u, int v){
P[u].pb(v), wi[v] = len[v] - len[u];
}
int build(){
sort(pi.begin(), pi.end(), [](int x, int y){ return dfn[x] < dfn[y]; });
pi.erase(unique(pi.begin(), pi.end()), pi.end());
int top; stk[top = 1] = pi[0], pi.erase(pi.begin());
for(auto &v : pi){
int lc = lca(stk[top], v);
while(dep[lc] < dep[stk[top - 1]]) link(stk[top - 1], stk[top]), --top;
if(lc != stk[top]){
link(lc, stk[top]), --top;
if(lc != stk[top]) stk[++top] = lc;
}
stk[++top] = v;
}
while(--top) link(stk[top], stk[top + 1]);
return stk[1];
}
void dfs(int u){
for(auto &v : P[u]){
dfs(v);
num[u][0] += num[v][0];
num[u][1] += num[v][1];
P[v].clear();
num[v][0] = num[v][1] = 0;
}
ans += num[u][0] * 1ll * num[u][1] * wi[u];
}
int main() {
ios::sync_with_stdio(0); cin.tie(0);
cin >> n >> m;
cin >> s + 1;
init();
for(int i = n; i >= 1; --i){
insert(s[i]);
pos[i] = last;
}
for(int i = 2; i <= cnt; ++i){
G[par[i]].pb(i);
}
dfs1(1, 0);
dfs2(1, 1);
while(m--){
int x, y; cin >> x >> y;
for(int i = 1; i <= x; ++i){
int u; cin >> u;
u = pos[u];
++num[u][0];
pi.pb(u);
}
for(int i = 1; i <= y; ++i){
int v; cin >> v;
v = pos[v];
++num[v][1];
pi.pb(v);
}
pi.pb(1);
int rt = build();
wi[rt] = 0, ans = 0, dfs(1);
P[rt].clear(), num[rt][0] = num[rt][1] = 0;
pi.clear();
cout << ans << "\n";
}
return 0;
}