bzoj3879
后缀数组+st表+单调栈
这道题是差异的加强版
看起来和差异差不多,但是询问的位置是不连续的,那么我们让他们连续就行。
把每个位置赋成rank值,因为lcp[i]表示rank=i和i+1的最长公共前缀,然后st表处理出相邻两个rank的lcp值,然后和差异一样,单调栈处理最左端和最右端的区间,乘起来就可以了。
注意要去重,而且不用模
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 500010; int n, top, k, tot, m; ll ans; int sa[N], rank[N], temp[N], l[N], r[N], st[N], lcp[N], Lcp[N], mn[N][24]; char s[N]; bool cp(int i, int j) { if(rank[i] != rank[j]) return rank[i] < rank[j]; int ri = i + k <= n ? rank[i + k] : -1; int rj = j + k <= n ? rank[j + k] : -1; return ri < rj; } void Sa() { for(int i = 1; i <= n; ++i) { sa[i] = i; rank[i] = s[i]; } for(k = 1; k <= n; k <<= 1) { sort(sa + 1, sa + n + 1, cp); temp[sa[1]] = 1; for(int i = 2; i <= n; ++i) temp[sa[i]] = temp[sa[i - 1]] + (cp(sa[i - 1], sa[i])); for(int i = 1; i <= n; ++i) rank[i] = temp[i]; } } void LLcp() { for(int i = 1; i <= n; ++i) rank[sa[i]] = i; int h = 0; for(int i = 1; i <= n; ++i) { int j = sa[rank[i] - 1]; if(rank[i] <= 1) continue; if(h) --h; for(; i + h <= n && j + h <= n; ++h) if(s[i + h] != s[j + h]) break; lcp[rank[i] - 1] = h; mn[rank[i] - 1][0] = h; } for(int i = 1; i <= 23; ++i) for(int j = n - 1; j; --j) { mn[j][i] = mn[j][i - 1]; if(j + (1 << (i - 1)) < n) mn[j][i] = min(mn[j][i], mn[j + (1 << (i - 1))][i - 1]); } } int query(int l, int r) { int x = log(r - l + 1) / log(2); int x1 = min(mn[l][x], mn[r - (1 << x) + 1][x]); return x1; } int main() { scanf("%d%d%s", &n, &m, s + 1); Sa(); LLcp(); while(m--) { int t; scanf("%d", &t); vector<int> q; while(t--) { int x; scanf("%d", &x); q.push_back(rank[x]); } sort(q.begin(), q.end()); q.erase(unique(q.begin(), q.end()), q.end()); int tot = 0; for(int i = 1; i < q.size(); ++i) Lcp[++tot] = query(q[i - 1], q[i] - 1); top = 0; for(int i = 1; i <= tot; ++i) { l[i] = r[i] = i; while(top && Lcp[i] < Lcp[st[top]]) { l[i] = l[st[top]]; r[st[top - 1]] = r[st[top]]; --top; } st[++top] = i; } while(top) { r[st[top - 1]] = r[st[top]]; --top; } ll ans = 0; for(int i = 1; i <= tot; ++i) ans += (ll)(i - l[i] + 1) * (ll)(r[i] - i + 1) * (ll)Lcp[i]; printf("%lld\n", ans); } return 0; }