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;
}

时间: 2024-11-09 00:28:42

bzoj3879的相关文章

BZOJ3879: SvT

后缀数组裸题,每次的查询单调栈扫一遍就完了.为什么要写虚后缀树= =后缀数组不是自带虚树的结构么= = #include<cstdio> #include<algorithm> using namespace std; typedef long long ll; const int N=5e5+5; typedef int arr[N]; arr sa,r,f[19]; void pre(char*s,int n){ static arr c,t; for(int i=0;i<

【BZOJ3879】SvT 后缀树+虚树

转载请注明出处谢谢:http://blog.csdn.net/vmurder/article/details/42806431 SVT什么意思? suffix virtual tree. 没有错!后缀虚树 好了,下面发一段以前的文字. 话说其实后缀数组分治能写,当时想shei了. Vn: 啊,水题. 一看到"后缀"和这数据范围,肯定后缀数组.后缀自动机.后缀树走起! 然后我们可以轻松构造出来一个后缀树,然后每次询问树形DP随便乱搞就能过了.但是这个时候显然会TLE,所以我们可以尝试利用

【bzoj3238】[Ahoi2013]差异 后缀数组+单调栈

题目描述 输入 一行,一个字符串S 输出 一行,一个整数,表示所求值 样例输入 cacao 样例输出 54 题解 后缀数组+单调栈,几乎同 bzoj3879 的后半部分. 我明显是做题做反了... 这里还是说一下这道题的做法. 先用后缀数组求出height. 然后由于有LCP(a,c)=min(LCP(a,b),LCP(b,c))(rank[a]<rank[b]<rank[c]),所以我们只需要知道排名相邻的两个后缀的LCP,而这就是height数组的定义. 转化为子问题:给出n个数,求所有子

Educational Codeforces Round 53 (Rated for Div. 2)G. Yet Another LCP Problem

题意:给串s,每次询问k个数a,l个数b,问a和b作为后缀的lcp的综合 题解:和bzoj3879类似,反向sam日神仙...lcp就是fail树上的lca.把点抠出来建虚树,然后在上面dp即可.(感觉之前写的svt什么玩意) //#pragma GCC optimize(2) //#pragma GCC optimize(3) //#pragma GCC optimize(4) //#pragma GCC optimize("unroll-loops") //#pragma comm