[BZOJ 3879] SvT
[题目链接]
https://www.lydsy.com/JudgeOnline/problem.php?id=3879
[算法]
首先 , 后缀树有一个很好的性质 :
两个后缀的LCP等于该字符串反串后缀树上所对应的两个节点的最近公共祖先u的right集合中最长的串 , 即maxlen(u)
注意到sigma(Ti)比较小 , 考虑首先构建后缀自动机 , 对于每组询问定位每个后缀在后缀树上的位置 , 建出虚树 , 在虚树上动态规划
时间复杂度 : O(|S| + T)
[代码]
#include<bits/stdc++.h> using namespace std; #ifndef LOCAL #define eprintf(...) fprintf(stderr, _VA_ARGS_) #else #define eprintf(...) 42 #endif typedef long long ll; typedef pair<int , int> pii; typedef pair<ll , int> pli; typedef pair<ll , ll> pll; typedef vector< int > VI; typedef long double ld; typedef unsigned long long ull; #define mp make_pair #define fi first #define se second const int N = 1e6 + 10; const int M = 3e6 + 10; const int ALPHA = 26; const int MAXLOG = 22; const ll P = 23333333333333333; int n , sz , timer , last , top , m; int dfn[N] , depth[N] , child[N][ALPHA] , a[M] , anc[N][MAXLOG] , mark[N] , size[N] , father[N] , stk[N] , lc[N] , dep[N]; char s[N]; ll ans; VI e[N] , b[N]; VI mem; template <typename T> inline void chkmax(T &x,T y) { x = max(x,y); } template <typename T> inline void chkmin(T &x,T y) { x = min(x,y); } template <typename T> inline void read(T &x) { T f = 1; x = 0; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0'; x *= f; } inline int new_node(int dep) { depth[++sz] = dep; memset(child[sz] , 0 , sizeof(child[sz])); father[sz] = 0; return sz; } inline void extend(int ch) { int np = new_node(depth[last] + 1); int p = last; while (child[p][ch] == 0) { child[p][ch] = np; p = father[p]; } if (child[p][ch] == np) father[np] = 1; else { int q = child[p][ch]; if (depth[q] == depth[p] + 1) father[np] = q; else { int nq = new_node(depth[p] + 1); father[nq] = father[q]; father[np] = father[q] = nq; memcpy(child[nq], child[q], sizeof(child[q])); while (child[p][ch] == q) { child[p][ch] = nq; p = father[p]; } } } last = np; } inline void dfs(int u , int par) { dfn[u] = ++timer; dep[u] = dep[par] + 1; anc[u][0] = par; for (int i = 1; i < MAXLOG; ++i) anc[u][i] = anc[anc[u][i - 1]][i - 1]; for (unsigned i = 0; i < e[u].size(); ++i) { dfs(e[u][i] , u); } } inline void work( ) { for (int i = 1; i <= sz; ++i) { e[father[i]].push_back(i); } dfs( 1 , 0 ); } inline int getlca(int x , int y) { if (dep[x] > dep[y]) swap(x , y); for (int i = MAXLOG - 1; i >= 0; --i) if (dep[anc[y][i]] >= dep[x]) y = anc[y][i]; if (x == y) return x; for (int i = MAXLOG - 1; i >= 0; --i) if (anc[x][i] != anc[y][i]) x = anc[x][i] , y = anc[y][i]; return anc[x][0]; } inline void insert(int u) { mem.push_back(u); if (top <= 1) { stk[++top] = u; return; } int lca = getlca(u , stk[top]); if (lca == stk[top]) { stk[++top] = u; return; } while (top > 1 && dfn[lca] <= dfn[stk[top - 1]]) { b[stk[top]].push_back(stk[top - 1]); b[stk[top - 1]].push_back(stk[top]); --top; } mem.push_back(lca); if (stk[top] != lca) { b[lca].push_back(stk[top]); b[stk[top]].push_back(lca); stk[top] = lca; } stk[++top] = u; } inline void calc(int u , int par) { ll cnt = 0; size[u] = 0; for (int i = 0; i < b[u].size(); ++i) { int v = b[u][i]; if (v == par) continue; calc(v , u); size[u] += size[v]; cnt = (cnt + 1LL * size[v] * (size[u] - size[v]) % P) % P; } if (mark[u]) { cnt = (cnt + size[u]) % P; ++size[u]; } ans = (ans + 1LL * depth[u] * cnt) % P; } inline bool cmp(int x , int y) { return dfn[x] < dfn[y]; } int main() { scanf("%d%d" , &n , &m); scanf("%s" , s + 1); reverse(s + 1 , s + n + 1); sz = last = 1; for (int i = 1; i <= n; ++i) { extend(s[i] - 'a'); lc[i] = last; } work(); while (m--) { int q; read(q); for (int i = 1; i <= q; ++i) { int x; read(x); a[i] = lc[n - x + 1]; mark[a[i]] = true; } a[++q] = 1; sort(a + 1 , a + q + 1 , cmp); top = 0; for (int i = 1; i <= q; ++i) { if (a[i] != a[i - 1]) insert(a[i]); } while (top > 1) { b[stk[top]].push_back(stk[top - 1]); b[stk[top - 1]].push_back(stk[top]); --top; } ans = 0; calc(1 , 0); printf("%lld\n" , ans); for (unsigned i = 0; i < mem.size(); ++i) { b[mem[i]].clear(); mark[mem[i]] = false; } mem.clear(); } return 0; }