SvT[BZOJ3879]

【题目描述】
有一个长度为n的仅包含小写字母的字符串(S),下标范围为([1,n]).

现在有若干组询问,对于每一个询问,我们给出若干个后缀(以其在(S)中出现的起始位置来表示),求这些后缀两两之间的LCP(LongestCommonPrefix)的长度之和.一对后缀之间的LCP长度仅统计一遍.

【输入格式】
第一行两个正整数(n,m),分别表示(S)的长度以及询问的次数.

接下来一行有一个字符串(S).

接下来有(m)组询问,对于每一组询问,均按照以下格式在一行内给出:

首先是一个整数(t),表示共有多少个后缀.接下来(t)个整数分别表示(t)个后缀在字符串(S)中的出现位置.

【输出格式】
对于每一组询问,输出一行一个整数,表示该组询问的答案.由于答案可能很大,仅需要输出这个答案对于(23333333333333333)(一个巨大的质数)取模的余数

题解

这不是和 差异「AHOI2013」 几乎一模一样吗。。。那题就相当于询问是(1sim n)的所有后缀 然后式子稍微变变形

传送门

这题也是一样 先建好后缀数组 对于每组询问 先按照后缀数组的(rnk)进行排序 然后去重 然后用ST表来求出排序后相邻两个后缀的LCP

剩下的单调栈DP就跟上面那题一样了 不想再敲一遍了 请点上面传送门 看解法一:后缀数组

唯一的区别就是多个ST表。。。

#include <bits/stdc++.h>
#define N 500005
using namespace std;
typedef long long ll;

const ll mod = 23333333333333333;
int n, m, t;
char s[500005];
int sa[N], sum[N], rnk[N], sa2[N], key[N], height[N], q[N], tmp[N], stk[N], top;
ll f[N], ans;

inline bool check(int *num, int a, int b, int l) {
	return num[a] == num[b] && num[a+l] == num[b+l];
}

inline void DA(int _m) {
	int i, j, p; 
	for (i = 1; i <= _m; i++) sum[i] = 0;
	for (i = 1; i <= n; i++) sum[rnk[i]=s[i]]++;
	for (i = 2; i <= _m; i++) sum[i] += sum[i-1];
	for (i = n; i >= 1; i--) sa[sum[rnk[i]]--] = i; 
	for (j = 1, p = 0; j <= n; j <<= 1, _m = p) {
		p = 0; for (i = n - j + 1; i <= n; i++) sa2[++p] = i;
		for (i = 1; i <= n; i++) if (sa[i] > j) sa2[++p] = sa[i] - j;
		for (i = 1; i <= n; i++) key[i] = rnk[sa2[i]];
		for (i = 1; i <= _m; i++) sum[i] = 0;
		for (i = 1; i <= n; i++) sum[key[i]]++;
		for (i = 2; i <= _m; i++) sum[i] += sum[i-1];
		for (i = n; i >= 1; i--) sa[sum[key[i]]--] = sa2[i];
		for (swap(rnk, sa2), p = 2, rnk[sa[1]] = 1, i = 2; i <= n; i++) {
			rnk[sa[i]] = check(sa2, sa[i-1], sa[i], j) ? p - 1 : p++;
		}
	}
}

inline void geth() {
	ll p = 0;
	for (int i = 1; i <= n; i++) rnk[sa[i]] = i;
	for (int i = 1; i <= n; i++) {
		if (p) p--;
		int j = sa[rnk[i]-1];
		while (s[i + p] == s[j + p]) p++;
		height[rnk[i]] = p;
	}
}

int st[N][21];

void preST() {
	for (int i = 1; i <= n; i++) st[i][0] = height[i];
	for (int l = 1; (1 << l) <= n; l++) {
		for (int i = 1; i <= n - (1 << l) + 1; i++) {
			st[i][l] = min(st[i][l-1], st[i+(1<<(l-1))][l-1]);
		}
	}
}

inline int query(int x, int y) {
	int l = log2(y - x + 1);
	return min(st[x][l], st[y - (1 << l) + 1][l]);
}

inline bool cmp(int x, int y) {
	return rnk[x] < rnk[y];
}

int main() {
	scanf("%d %d %s", &n, &m, s + 1);
	DA(128); 
	geth();
	preST();
	for (int i = 1; i <= m; i++) {
		scanf("%d", &t);
		ans = 0;
		for (int j = 1; j <= t; j++) scanf("%d", &q[j]);
		sort(q + 1, q + t + 1, cmp);
		t = unique(q + 1, q + t + 1) - q - 1;
		for (int j = 2; j <= t; j++) {
			tmp[j] = query(rnk[q[j-1]] + 1, rnk[q[j]]);
		}
		stk[top=1] = 1; 
		for (int j = 2; j <= t; j++) {
			while (top && tmp[stk[top]] > tmp[j]) top--;
			f[j] = f[stk[top]] + (j - stk[top]) * tmp[j];
			stk[++top] = j;
			ans += f[j];	
		}
		printf("%lld
", ans);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/ak-dream/p/AK_DREAM62.html