[洛谷P4248][AHOI2013]差异

题目大意:给一个长度为$n$的字符串,求:

$$
\sum\limits_{1\leqslant i<j\leqslant n}|suf_i|+|suf_j|-2\times lcp(suf_i,suf_j)
$$

题解:建一棵后缀树,这个式子就成了后缀树上所有后缀之间的距离(后缀树可以把字符串反着加入后缀自动机得到的$fail$数组而来),然后有两种做法:

1. 把$\sum\limits_{1\leqslant i<j\leqslant n}|suf_i|+|suf_j|$直接求出来
$$
\begin{align*}
&\sum\limits_{1\leqslant i<j\leqslant n}|suf_i|+|suf_j|\\
=&\sum\limits_{1\leqslant i<j\leqslant n}i+j\\
=&\dfrac{n(n+1)(n-1)} 2
\end{align*}
$$
然后对每个点考虑它作为$lca$的贡献

2. 直接考虑每条边的贡献

卡点:

 

C++ Code:(方法一)

#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxn 500010

namespace SAM {
#define N (maxn << 1)
	int head[N], cnt;
	struct Edge {
		int to, nxt;
	} e[N];
	inline void addedge(int a, int b) {
		e[++cnt] = (Edge) {b, head[a]}; head[a] = cnt;
	}

	int R[N], fail[N], nxt[N][26];
	int lst = 1, idx = 1;
	int sz[N];
	void append(char __ch) {
		int ch = __ch - 'a';
		int p = lst, np = lst = ++idx;
		R[np] = R[p] + 1; sz[np] = 1;
		for (; p && !nxt[p][ch]; p = fail[p]) nxt[p][ch] = np;
		if (!p) fail[np] = 1;
		else {
			int q = nxt[p][ch];
			if (R[q] == R[p] + 1) fail[np] = q;
			else {
				int nq = ++idx;
				std::copy(nxt[q], nxt[q] + 26, nxt[nq]);
				fail[nq] = fail[q], R[nq] = R[p] + 1, fail[np] = fail[q] = nq;
				for (; p && nxt[p][ch] == q; p = fail[p]) nxt[p][ch] = nq;
			}
		}
	}

	long long ans;
	void dfs(int u) {
		long long tmp = 0;
		for (int i = head[u]; i; i = e[i].nxt) {
			int v = e[i].to;
			dfs(v);
			tmp += static_cast<long long> (sz[u]) * sz[v];
			sz[u] += sz[v];
		}
		ans += 2 * tmp * R[u];
	}
	long long work() {
		for (int i = 2; i <= idx; i++) addedge(fail[i], i);
		dfs(1);
		return ans;
	}
#undef N
}

int n;
char s[maxn];
long long ans;
int main() {
	scanf("%s", s + 1);
	n = strlen(s + 1);
	for (int i = n; i; i--) SAM::append(s[i]);
	ans = static_cast<long long> (n - 1) * n * (n + 1) / 2;
	ans -= SAM::work();
	printf("%lld\n", ans);
	return 0;
}

  

C++ Code:(方法二)

#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxn 500010

long long ans;
int n;
namespace SAM {
#define N (maxn << 1)
	int head[N], cnt;
	struct Edge {
		int to, nxt;
	} e[N];
	inline void addedge(int a, int b) {
		e[++cnt] = (Edge) {b, head[a]}; head[a] = cnt;
	}

	int R[N], fail[N], nxt[N][26];
	int lst = 1, idx = 1;
	int sz[N];
	void append(char __ch) {
		int ch = __ch - 'a';
		int p = lst, np = lst = ++idx;
		R[np] = R[p] + 1; sz[np] = 1;
		for (; p && !nxt[p][ch]; p = fail[p]) nxt[p][ch] = np;
		if (!p) fail[np] = 1;
		else {
			int q = nxt[p][ch];
			if (R[q] == R[p] + 1) fail[np] = q;
			else {
				int nq = ++idx;
				std::copy(nxt[q], nxt[q] + 26, nxt[nq]);
				fail[nq] = fail[q], R[nq] = R[p] + 1, fail[np] = fail[q] = nq;
				for (; p && nxt[p][ch] == q; p = fail[p]) nxt[p][ch] = nq;
			}
		}
	}

	void dfs(int u) {
		for (int i = head[u]; i; i = e[i].nxt) {
			int v = e[i].to;
			dfs(v);
			sz[u] += sz[v];
			ans += static_cast<long long> (n - sz[v]) * (sz[v]) * (R[v] - R[u]);
		}
	}
	void work() {
		for (int i = 2; i <= idx; i++) addedge(fail[i], i);
		dfs(1);
	}
#undef N
}

char s[maxn];
int main() {
	scanf("%s", s + 1);
	n = strlen(s + 1);
	for (int i = n; i; i--) SAM::append(s[i]);
	SAM::work();
	printf("%lld\n", ans);
	return 0;
}

  

posted @ 2018-12-22 15:11  Memory_of_winter  阅读(195)  评论(0编辑  收藏  举报