• Bzoj3879:SvT——后缀数组+RMQ+单调栈


    题面

       Bzoj3879

    解析

       求后缀的LCP显然可以用后缀数组

      考虑到任意两个后缀的$LCP$是它们在$sa$数组中两个之间的最小的$hei$, 即$LCP(i, j) = minleft { hei[k] ight }(rk[i] < k leqslant rk[j], rk[i] < rk[j])$, 所以我们把每一组询问按照$rk$排序,再去重,用RMQ求出此时询问中相邻两个后缀的$LCP$, 记为$height$,  我这里的$height[i]$是指询问中$i$与$i-1$的$LCP$

      再考虑每一个后缀对答案的贡献,以它为答案的区间,左端点在上一个$height$比它小的后缀与它之间,右端点在它与下一个$height$比它小的后缀之间,注意一下端点选与不选的细节。这个显然可以用单调栈快速维护,用乘法原理搞一下,再加起来就行

     代码:

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    const int maxn = 500004;
    const ll mod = 23333333333333333;
    
    int n, Q;
    char s[maxn];
    int q[maxn], sa[maxn], rk[maxn], hei[maxn], fir[maxn], sec[maxn], c[maxn];
    ll ans;
    
    void Build_SA()
    {
        int m = 128;
        for(int i = 1; i <= n; ++i)
            fir[i] = s[i];
        for(int i = 0; i <= m; ++i)
            c[i] = 0;
        for(int i = 1; i <= n; ++i)
            c[fir[i]] ++;
        for(int i = 1; i <= m; ++i)
            c[i] += c[i-1];
        for(int i = n; i; --i)
            sa[c[fir[i]]--] = i;
        for(int k = 1; k <= n; k <<= 1)
        {
            int t = 0;
            for(int i = n - k + 1; i <= n; ++i)
                sec[++t] = i;
            for(int i = 1; i <= n; ++i)
                if(sa[i] > k)
                    sec[++t] = sa[i] - k;
            for(int i = 0; i <= m; ++i)
                c[i] = 0;
            for(int i = 1; i <= n; ++i)
                c[fir[sec[i]]] ++;
            for(int i = 1; i <= m; ++i)
                c[i] += c[i-1];
            for(int i = n; i; --i)
                sa[c[fir[sec[i]]]--] = sec[i], sec[i] = 0;
            for(int i = 1; i<= n; ++i)
                swap(fir[i], sec[i]);
            t = 0;
            fir[sa[1]] = ++t;
            for(int i = 2; i <= n; ++i)
                if(sec[sa[i]] != sec[sa[i-1]] || sec[sa[i]+k] != sec[sa[i-1]+k])
                    fir[sa[i]] = ++t;
                else
                    fir[sa[i]] = t;
            if(t >= n)
                break;
            m = max(m, t);
        }
        for(int i = 1; i <= n; ++i)
            rk[sa[i]] = i;
    }
    
    void Get_hei()
    {
        int h = 0;
        for(int i = 1; i <= n; ++i)
        {
            int t = sa[rk[i]-1];
            while(s[i+h] == s[t+h])    h++;
            hei[rk[i]] = h;
            h = max(0, h-1);
        }
    }
    
    int mn[20][maxn], lg[maxn];
    
    void RMQ()
    {
        lg[0] = -1;
        for(int i = 1; i <= n; ++i)
            lg[i] = lg[i>>1] + 1;
        for(int i = 1; i <= n; ++i)
            mn[0][i] = hei[i];
        for(int i = 1; i <= 16; ++i)
            for(int j = 1; j <= n; ++j)
                if(j + (1<<i) - 1 <= n)
                    mn[i][j] = min(mn[i-1][j], mn[i-1][j+(1<<(i-1))]);
    }
    
    bool cmp(int x, int y)
    {
        return rk[x] < rk[y];
    }
    
    int stak[maxn], top, height[maxn], l[maxn], r[maxn];
    
    int main()
    {
        scanf("%d%d", &n, &Q);
        scanf("%s", s+1);
        Build_SA();
        Get_hei();
        RMQ();
        while(Q--)
        {
            ans = 0;
            int tot;
            scanf("%d", &tot);
            for(int i = 1; i <= tot; ++i)
                scanf("%d", &q[i]);
            sort(q + 1, q + tot + 1, cmp);
            tot = unique(q + 1, q + tot + 1) - q - 1;
            int h = 0;
            for(int i = 2; i <= tot; ++i)
            {
                int t = q[i-1];
                height[i] = min(mn[lg[rk[q[i]] - rk[t]]][rk[t] + 1], mn[lg[rk[q[i]]-rk[t]]][rk[q[i]] - (1<<lg[rk[q[i]] - rk[t]]) + 1]);
            }
            for(int i = 1; i <= tot; ++i)
            {
                while(height[stak[top]] > height[i] && top)
                    r[stak[top--]] = i - 1;
                l[i] = max(stak[top], 1);
                stak[++top] = i;
            }
            while(top)
                r[stak[top--]] = tot;
            for(int i = 1; i <= tot; ++i)
                ans = (ans + (1LL * (r[i] - i + 1) * (i - l[i]) * height[i] % mod)) % mod;
            printf("%lld
    ", ans);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    Visual Studio的输出窗口上输出调试信息的函数
    std::min error C2059: 语法错误:“::” 的解决方法
    error C2872: “flann”: 不明确的符号 --- PCL 与OpenCV2 的flann命名空间冲突问题的解决方法
    VS编译器中设置 输出窗口 只显示error,不显示warning 要如何配置
    nginx.conf的完整配置说明
    Nginx基本配置、性能优化指南
    Apache手册
    Apache 配置虚拟主机三种方式
    Linux常用命令汇总
    Linux下安装Apache
  • 原文地址:https://www.cnblogs.com/Joker-Yza/p/11344027.html
Copyright © 2020-2023  润新知