zoukankan      html  css  js  c++  java
  • 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
    ", ans);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    PHP realpath() 函数
    PHP getcwd() 函数
    移动加权平均&全月平均
    Nginx禁止访问某个目录
    foreach ($array as $key=>$value)
    php中$_SERVER变量的意义及用法说明
    [JS代码]如何判断ipad或者iphone是否为横屏或者竖屏 portrait或者landscape
    分享10个实用的超绚CSS3按钮设计
    分享三款非常实用的免费信息图
    分享200个免费的倒影效果移动设备及网站图标下载
  • 原文地址:https://www.cnblogs.com/19992147orz/p/7190254.html
Copyright © 2011-2022 走看看