首页 > 代码库 > bzoj3879

bzoj3879

后缀数组+st表+单调栈

这道题是差异的加强版

看起来和差异差不多,但是询问的位置是不连续的,那么我们让他们连续就行。

把每个位置赋成rank值,因为lcp[i]表示rank=i和i+1的最长公共前缀,然后st表处理出相邻两个rank的lcp值,然后和差异一样,单调栈处理最左端和最右端的区间,乘起来就可以了。

注意要去重,而且不用模

技术分享
#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;
}
View Code

 

bzoj3879