洛谷 P7409 SvT

洛谷传送门

考虑对反串建 SAM,设 \([i, n]\) 的后缀对应 SAM 的点是 \(a_i\)

那么 \(\text{lcp}(s[i : n], s[j : n]) = \text{len}(\text{lca}(a_i, a_j))\)

于是问题变成了,给定一些点,统计两两 \(\text{lca}\) 点权之和。

考虑建虚树,枚举每个点 \(u\) 作为 \(\text{lca}\) 的次数。设当前已经考虑过的儿子的 \(sz\) 之和为 \(s\),那么 \(v\) 作为子树会有 \(s \times sz_v\) 次数的贡献。再乘上 \(len_u\) 累加进答案即可。

若使用 dfs 序 LCA,时间复杂度 \(O((n + \sum t) \log n)\)

code
// Problem: P7409 SvT
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P7409
// Memory Limit: 512 MB
// Time Limit: 3000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<int, int> pii;

const int maxn = 1000100;
const int logn = 22;

int n, m, lsh[maxn << 1], b[maxn], a[maxn * 3];
char s[maxn];
vector<int> G[maxn], T[maxn];

struct SAM {
	int lst, tot, fa[maxn], ch[maxn][26], len[maxn];
	
	inline void init() {
		for (int i = 1; i <= tot; ++i) {
			fa[i] = len[i] = 0;
			mems(ch[i], 0);
		}
		lst = tot = 1;
	}
	
	inline void insert(int c) {
		int u = ++tot, p = lst;
		len[u] = len[p] + 1;
		lst = u;
		for (; p && !ch[p][c]; p = fa[p]) {
			ch[p][c] = u;
		}
		if (!p) {
			fa[u] = 1;
			return;
		}
		int q = ch[p][c];
		if (len[q] == len[p] + 1) {
			fa[u] = q;
			return;
		}
		int nq = ++tot;
		fa[nq] = fa[q];
		len[nq] = len[p] + 1;
		memcpy(ch[nq], ch[q], sizeof(ch[nq]));
		fa[u] = fa[q] = nq;
		for (; p && ch[p][c] == q; p = fa[p]) {
			ch[p][c] = nq;
		}
	}
} sam;

int st[logn][maxn], dfn[maxn], tim;

inline int get(int i, int j) {
	return dfn[i] < dfn[j] ? i : j;
}

inline int qlca(int x, int y) {
	if (x == y) {
		return x;
	}
	x = dfn[x];
	y = dfn[y];
	if (x > y) {
		swap(x, y);
	}
	++x;
	int k = __lg(y - x + 1);
	return get(st[k][x], st[k][y - (1 << k) + 1]);
}

void dfs(int u) {
	dfn[u] = ++tim;
	for (int v : G[u]) {
		dfs(v);
		st[0][dfn[v]] = u;
	}
}

ll f[maxn], ans;

void dfs2(int u) {
	for (int v : T[u]) {
		dfs2(v);
		ans += f[u] * f[v] * sam.len[u];
		f[u] += f[v];
	}
}

void solve() {
	scanf("%d%d%s", &n, &m, s + 1);
	sam.init();
	for (int i = n; i; --i) {
		sam.insert(s[i] - 'a');
		b[i] = sam.lst;
	}
	for (int i = 2; i <= sam.tot; ++i) {
		G[sam.fa[i]].pb(i);
	}
	dfs(1);
	for (int j = 1; (1 << j) <= tim; ++j) {
		for (int i = 1; i + (1 << j) - 1 <= tim; ++i) {
			st[j][i] = get(st[j - 1][i], st[j - 1][i + (1 << (j - 1))]);
		}
	}
	while (m--) {
		int len, tot = 0;
		scanf("%d", &len);
		for (int i = 1; i <= len; ++i) {
			scanf("%d", &a[i]);
			a[i] = b[a[i]];
		}
		sort(a + 1, a + len + 1);
		len = unique(a + 1, a + len + 1) - a - 1;
		sort(a + 1, a + len + 1, [&](const int &x, const int &y) {
			return dfn[x] < dfn[y];
		});
		for (int i = 1; i <= len; ++i) {
			lsh[++tot] = a[i];
			if (i > 1) {
				lsh[++tot] = qlca(a[i - 1], a[i]);
			}
		}
		sort(lsh + 1, lsh + tot + 1);
		tot = unique(lsh + 1, lsh + tot + 1) - lsh - 1;
		sort(lsh + 1, lsh + tot + 1, [&](const int &x, const int &y) {
			return dfn[x] < dfn[y];
		});
		for (int i = 2; i <= tot; ++i) {
			T[qlca(lsh[i - 1], lsh[i])].pb(lsh[i]);
		}
		for (int i = 1; i <= len; ++i) {
			f[a[i]] = 1;
		}
		ans = 0;
		dfs2(lsh[1]);
		printf("%lld\n", ans % 23333333333333333LL);
		for (int i = 1; i <= tot; ++i) {
			vector<int>().swap(T[lsh[i]]);
			f[lsh[i]] = 0;
		}
	}
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}
posted @ 2024-01-10 10:06  zltzlt  阅读(50)  评论(0)    收藏  举报