题面很清楚,不概括了。
思路
后缀树 + 树剖。
套路题。
看到 lcp 考虑转化成后缀树上求 lca,这里可以用 SAM 构造反串的 parent tree 解撅(f**k u ukk)
于是问题转化成:
每次给定两个集合 \(A, B\),求 \(\sum\limits_{i \in A} \sum\limits_{j \in B} \operatorname{depth}(\operatorname{lca}(i, j))\)
这个和 【题解】P5305 [GXOI/GZOI2019]旧词 是一个套路,考虑树剖就行。
代码
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 5;
const int maxl = 2e5 + 5;
const int maxe = maxn << 1;
const int sam_sz = maxn << 1;
struct node
{
int to, nxt;
} edge[maxe];
int n, m, q, cnt;
int idx[maxn];
int a[maxl], b[maxl];
int head[sam_sz], fa[sam_sz], son[sam_sz], top[sam_sz];
int dep[sam_sz], dfn[sam_sz], rk[sam_sz], sz[sam_sz], w[sam_sz];
char str[maxn];
namespace SAM
{
int lst = 1, cur = 1;
int len[sam_sz], fa[sam_sz], son[sam_sz][26];
void copy_node(int to, int from)
{
len[to] = len[from], fa[to] = fa[from];
memcpy(son[to], son[from], sizeof(son[to]));
}
void insert(int c)
{
int p = lst, np = lst = ++cur;
len[np] = len[p] + 1;
for ( ; p && (!son[p][c]); p = fa[p]) son[p][c] = np;
if (!p) fa[np] = 1;
else
{
int q = son[p][c];
if (len[q] == len[p] + 1) fa[np] = q;
else
{
int nq = ++cur;
copy_node(nq, q);
len[nq] = len[p] + 1;
fa[q] = fa[np] = nq;
for ( ; p && (son[p][c] == q); p = fa[p]) son[p][c] = nq;
}
}
}
}
namespace SGT
{
struct node
{
int l, r, mul, lazy;
ll sum;
} tree[sam_sz << 2];
void push_up(int k) { tree[k].sum = tree[k << 1].sum + tree[k << 1 | 1].sum; }
void push_down(int k)
{
if (!tree[k].lazy) return;
tree[k << 1].sum += 1ll * tree[k << 1].mul * tree[k].lazy;
tree[k << 1 | 1].sum += 1ll * tree[k << 1 | 1].mul * tree[k].lazy;
tree[k << 1].lazy += tree[k].lazy, tree[k << 1 | 1].lazy += tree[k].lazy;
tree[k].lazy = 0;
}
void build(int k, int l, int r)
{
tree[k].l = l, tree[k].r = r;
if (l == r)
{
tree[k].mul = w[l];
return;
}
int mid = (l + r) >> 1;
build(k << 1, l, mid);
build(k << 1 | 1, mid + 1, r);
tree[k].mul = tree[k << 1].mul + tree[k << 1 | 1].mul;
}
void update(int k, int l, int r, int w)
{
if ((tree[k].l >= l) && (tree[k].r <= r))
{
tree[k].sum += 1ll * tree[k].mul * w;
tree[k].lazy += w;
return;
}
push_down(k);
int mid = (tree[k].l + tree[k].r) >> 1;
if (l <= mid) update(k << 1, l, r, w);
if (r > mid) update(k << 1 | 1, l, r, w);
push_up(k);
}
ll query(int k, int l, int r)
{
if ((tree[k].l >= l) && (tree[k].r <= r)) return tree[k].sum;
push_down(k);
int mid = (tree[k].l + tree[k].r) >> 1;
ll res = 0;
if (l <= mid) res += query(k << 1, l, r);
if (r > mid) res += query(k << 1 | 1, l, r);
return res;
}
void updatep(int u, int w)
{
while (u)
{
update(1, dfn[top[u]], dfn[u], w);
u = fa[top[u]];
}
}
ll queryp(int u)
{
ll res = 0;
while (u)
{
res += query(1, dfn[top[u]], dfn[u]);
u = fa[top[u]];
}
return res;
}
}
void add_edge(int u, int v)
{
cnt++;
edge[cnt].to = v;
edge[cnt].nxt = head[u];
head[u] = cnt;
}
void dfs1(int u, int f)
{
fa[u] = f;
dep[u] = dep[f] + 1;
sz[u] = 1;
for (int i = head[u]; i; i = edge[i].nxt)
{
int v = edge[i].to;
dfs1(v, u);
sz[u] += sz[v];
if (sz[v] > sz[son[u]]) son[u] = v;
}
}
void dfs2(int u, int t)
{
top[u] = t;
dfn[u] = ++cnt;
rk[cnt] = u;
w[cnt] = SAM::len[u] - SAM::len[SAM::fa[u]];
if (son[u]) dfs2(son[u], t);
for (int i = head[u]; i; i = edge[i].nxt)
{
int v = edge[i].to;
if (v != son[u]) dfs2(v, v);
}
}
int main()
{
// freopen("CF1073G_0.in", "r", stdin);
// freopen("CF1073G_0.out", "w", stdout);
scanf("%d%d%s", &n, &q, str + 1);
for (int i = n; i; i--) SAM::insert(str[i] - 'a'), idx[i] = SAM::lst;
m = SAM::cur;
for (int i = 2; i <= m; i++) add_edge(SAM::fa[i], i);
cnt = 0;
dfs1(1, 0);
dfs2(1, 1);
SGT::build(1, 1, m);
while (q--)
{
int ki, li;
ll ans = 0;
scanf("%d%d", &ki, &li);
for (int i = 1; i <= ki; i++)
{
scanf("%d", &a[i]);
SGT::updatep(idx[a[i]], 1);
}
for (int i = 1; i <= li; i++)
{
scanf("%d", &b[i]);
ans += SGT::queryp(idx[b[i]]);
}
// for (int i = 1; i <= ki; i++) ans += SGT::queryp(idx[a[i]]);
for (int i = 1; i <= ki; i++) SGT::updatep(idx[a[i]], -1);
printf("%lld\n", ans);
}
return 0;
}
标签:const,LCP,SAM,int,题解,son,CF1073G
From: https://www.cnblogs.com/lingspace/p/cf1073g.html