[BZOJ 3879] SvT

[题目链接]

[算法]

首先 ， 后缀树有一个很好的性质 :

两个后缀的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;
for (int i = 1; i <= q; ++i) {
int 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;

}

posted @ 2019-05-26 22:41  evenbao  阅读(321)  评论(0编辑  收藏  举报