P4248 [AHOI2013]差异

Posted Jozky86

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P4248 [AHOI2013]差异相关的知识,希望对你有一定的参考价值。

P4248 [AHOI2013]差异

题意:

∑ 1 ≤ i < j ≤ n l e n ( T i ) + l e n ( T j ) − 2 ∗ l c p ( T i , T j ) \\sum_{1\\leq i<j\\leq n}len(T_{i})+len(T_{j})-2*lcp(T_{i},T_{j}) 1i<jnlen(Ti)+len(Tj)2lcp(Ti,Tj)

题解:

∑ 1 ≤ i < j ≤ n l e n ( T i ) + l e n ( T j ) \\sum_{1\\leq i<j\\leq n}len(T_{i})+len(T_{j}) 1i<jnlen(Ti)+len(Tj)这部分好说,就是n * (n - 1) * (n + 1) / 2
对于lcp(Ti,Tj),就是min(Height[l+1]…Height[r] ),l = 后缀suf(i)的排名,r = 后缀suf(j)的排名
那我们就要计算所有区间的权值和,每个区间的权值为该区间的最小值。
那我们可以考虑对于每个height[i],他给多少区间做了贡献,可以用单调栈
这个区间贡献计算方式:
如果第i个数是l到r中最小的
贡献就是:(i-l+1)*(r-i+1)
处理出l和r,用单调栈
注意:l表示小于i的第一个,r表示小于等于i的第一个
左开右闭
这里卡了我一个多小时。。

代码:

// Problem: P4248 [AHOI2013]差异
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P4248
// Memory Limit: 500 MB
// Time Limit: 2000 ms
// Data:2021-08-22 15:25:53
// By Jozky

#include <bits/stdc++.h>
#include <unordered_map>
#define debug(a, b) printf("%s = %d\\n", a, b);
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
clock_t startTime, endTime;
//Fe~Jozky
const ll INF_ll= 1e18;
const int INF_int= 0x3f3f3f3f;
void read(){};
template <typename _Tp, typename... _Tps> void read(_Tp& x, _Tps&... Ar)
{
    x= 0;
    char c= getchar();
    bool flag= 0;
    while (c < '0' || c > '9')
        flag|= (c == '-'), c= getchar();
    while (c >= '0' && c <= '9')
        x= (x << 3) + (x << 1) + (c ^ 48), c= getchar();
    if (flag)
        x= -x;
    read(Ar...);
}
template <typename T> inline void write(T x)
{
    if (x < 0) {
        x= ~(x - 1);
        putchar('-');
    }
    if (x > 9)
        write(x / 10);
    putchar(x % 10 + '0');
}
void rd_test()
{
#ifdef LOCAL
    startTime= clock();
    freopen("in.txt", "r", stdin);
#endif
}
void Time_test()
{
#ifdef LOCAL
    endTime= clock();
    printf("\\nRun Time:%lfs\\n", (double)(endTime - startTime) / CLOCKS_PER_SEC);
#endif
}
const int MAXN= 1000005;

char ch[MAXN], all[MAXN];
int sa[MAXN], rk[MAXN], height[MAXN], tax[MAXN], tp[MAXN], a[MAXN], n, m;
char str[MAXN];
//rk[i] 第i个后缀的排名; sa[i] 排名为i的后缀位置; height[i] 排名为i的后缀与排名为(i-1)的后缀的LCP
//tax[i] 计数排序辅助数组; tp[i] rk的辅助数组(计数排序中的第二关键字),与sa意义一样。
//a为原串
void RSort()
{
    //rk第一关键字,tp第二关键字。
    for (int i= 0; i <= m; i++)
        tax[i]= 0;
    for (int i= 1; i <= n; i++)
        tax[rk[tp[i]]]++;
    for (int i= 1; i <= m; i++)
        tax[i]+= tax[i - 1];
    for (int i= n; i >= 1; i--)
        sa[tax[rk[tp[i]]]--]= tp[i]; //确保满足第一关键字的同时,再满足第二关键字的要求
} //计数排序,把新的二元组排序。

int cmp(int* f, int x, int y, int w)
{
    return f[x] == f[y] && f[x + w] == f[y + w];
}
//通过二元组两个下标的比较,确定两个子串是否相同

void Suffix()
{
    //sa
    for (int i= 1; i <= n; i++)
        rk[i]= a[i], tp[i]= i;
    m= 127, RSort(); //一开始是以单个字符为单位,所以(m = 127)

    for (int w= 1, p= 1, i; p < n; w+= w, m= p) { //把子串长度翻倍,更新rk

        //w 当前一个子串的长度; m 当前离散后的排名种类数
        //当前的tp(第二关键字)可直接由上一次的sa的得到
        for (p= 0, i= n - w + 1; i <= n; i++)
            tp[++p]= i; //长度越界,第二关键字为0
        for (i= 1; i <= n; i++)
            if (sa[i] > w)
                tp[++p]= sa[i] - w;

        //更新sa值,并用tp暂时存下上一轮的rk(用于cmp比较)
        RSort(), swap(rk, tp), rk[sa[1]]= p= 1;

        //用已经完成的sa来更新与它互逆的rk,并离散rk
        for (i= 2; i <= n; i++)
            rk[sa[i]]= cmp(tp, sa[i], sa[i - 1], w) ? p : ++p;
    }
    //离散:把相等的字符串的rk设为相同。
    //LCP
    int j, k= 0;
    for (int i= 1; i <= n; height[rk[i++]]= k)
        for (k= k ? k - 1 : k, j= sa[rk[i] - 1]; a[i + k] == a[j + k]; ++k)
            ;
    //这个知道原理后就比较好理解程序
}

void Init()
{
    scanf("%s", str);
    n= strlen(str);
    for (int i= 0; i < n; i++)
        a[i + 1]= str[i];
}
int st[MAXN], l[MAXN];
int main()
{
    //rd_test();
    Init();
    Suffix();
    int tail= 0;

    ll sum= 1ll * n * (n - 1) * (n + 1) / 2;
    height[0]= height[n + 1]= 0;
    //st[tail= 1]= 0;=
    st[0]= 1;
    for (int i= 1; i <= n; i++) {
        while (tail && height[st[tail]] >= height[i])
            tail--;
        int pos= st[tail];

        l[i]= (i - pos);
        st[++tail]= i;
    }
    tail= 0;
    st[0]= n + 1;
    for (int i= n; i >= 1; i--) {
        while (tail && height[st[tail]] > height[i])
            tail--;
        int pos= st[tail];
        sum-= (2ll * l[i] * (pos - i)) * height[i];
        st[++tail]= i;
    }
    cout << sum;
    //Time_test();
}
/*
ll sum= 1ll * n * (n - 1) * (n + 1) / 2;
    height[0]= height[n + 1]= 0;
    //st[tail= 1]= 0;
    for (int i= 1; i <= n; i++) {
        while (tail && height[st[tail]] > height[i])
            tail--;
        int pos= st[tail];
        f[i]= f[pos] + 1ll * (i - pos) * height[i];
        // sum-= 2ll * (i - pos) * height[i];
        sum-= 2 * f[i];
        st[++tail]= i;
    }
*/

以上是关于P4248 [AHOI2013]差异的主要内容,如果未能解决你的问题,请参考以下文章

P4248 [AHOI2013]差异

[Ahoi2013]差异

Bzoj3238: [Ahoi2013]差异

[BZOJ3238][Ahoi2013]差异

bzoj 3238: [Ahoi2013]差异 -- 后缀数组

BZOJ 3238 3238: [Ahoi2013]差异(SAM)