• BZOJ 3238: [Ahoi2013]差异 (后缀数组+单调栈)


    题目要求在这里插入图片描述
    所以Ans=n(n1)(n+1)221i<jnlcp(Ti,Tj)Ans=frac{n*(n-1)*(n+1)}{2}-2*sum_{1le i<jle n}lcp(T_i, T_j)

    后面要减去的部分是两两后缀的lcplcp,那么做了后缀数组之后就是Ans=n(n1)(n+1)221<ijnMin(Heighti,Heightj)Ans=frac{n*(n-1)*(n+1)}{2}-2*sum_{1<ile jle n}Min(Height_i, Height_j)

    后面其实就是[2,n][2,n]里面所有区间的最小值加起来再乘22.

    这样我们考虑一个值可以作为最小值能够拓展的最左端和最右端,就可以算出这个值对答案的贡献.这个就单调栈就行了.具体见代码.

    …mdzz又双叒叕缀写错后缀(chuo)数组了.

    CODE

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int MAXN = 5e5+5;
    char s[MAXN];
    int n, x[MAXN], y[MAXN], c[MAXN], sa[MAXN], rk[MAXN], ht[MAXN];
    inline void Get_Sa(int m) {
        for(int i = 1; i <= m; ++i) c[i] = 0;
        for(int i = 1; i <= n; ++i) ++c[x[i]=s[i]-'a'+1];
        for(int i = 2; i <= m; ++i) c[i] += c[i-1];
        for(int i = n; i >= 1; --i) sa[c[x[i]]--] = i;
        for(int k = 1; k <= n; k<<=1) {
            int p = 0;
            for(int i = n-k+1; i <= n; ++i) y[++p] = i;
            for(int i = 1; i <= n; ++i) if(sa[i]>k) y[++p] = sa[i]-k;
            for(int i = 1; i <= m; ++i) c[i] = 0;
            for(int i = 1; i <= n; ++i) ++c[x[i]];
            for(int i = 2; i <= m; ++i) c[i] += c[i-1];
            for(int i = n; i >= 1; --i) sa[c[x[y[i]]]--] = y[i];
            swap(x, y);
            x[sa[1]] = p = 1;
            for(int i = 2; i <= n; ++i)
                x[sa[i]] = (y[sa[i]] == y[sa[i-1]] && y[sa[i]+k] == y[sa[i-1]+k]) ? p : ++p;
            if((m=p) == n) break;
        }
        for(int i = 1; i <= n; ++i) rk[sa[i]] = i;
    }
    inline void Get_Ht() {
        for(int i = 1, k = 0, j; i <= n; ++i) if(rk[i] > 1) {
            j = sa[rk[i]-1]; k = k ? k-1 : 0;
            while(i+k <= n && j+k <= n && s[i+k] == s[j+k]) ++k;
            ht[rk[i]] = k;
        }
    }
    int st[MAXN], top, L[MAXN], R[MAXN];
    int main() {
    	scanf("%s", s+1);
    	n = strlen(s+1);
    	Get_Sa(26); Get_Ht();
        L[st[top=1] = 1] = 0;
    	for(int i = 2; i <= n; ++i) {
            while(top && ht[st[top]] > ht[i]) R[st[top--]] = i;
            L[i] = st[top]; st[++top] = i;
    	}
    	while(top) R[st[top--]] = n+1;
        LL ans = 1ll * (n-1) * n * (n+1) / 2;
    	for(int i = 2; i <= n; ++i)
            ans -= 2ll * (i-L[i]) * (R[i]-i) * ht[i];
    	printf("%lld
    ", ans);
    }
    
    
    
  • 相关阅读:
    LeetCode 297. 二叉树的序列化与反序列化
    LeetCode 14. 最长公共前缀
    LeetCode 1300. 转变数组后最接近目标值的数组和
    bigo一面凉经
    LeetCode 128.最长连续序列
    LeetCode中二分查找算法的变种
    LeetCode 93. 复原IP地址
    LeetCode 1004. 最大连续1的个数 III
    LeetCode 1282. 用户分组
    多线程理解
  • 原文地址:https://www.cnblogs.com/Orz-IE/p/12039313.html
Copyright © 2020-2023  润新知