【AHOI 2013】差异

\((\displaystyle \sum^{}_{1<=i<j<=n}{i+j})-2*\displaystyle \sum^{}_{1<=i<j<=n}{lcp(suf(i),suf(j))}\)

\(=(n-1)*Σi-2*\displaystyle \sum^{}_{1<=i<j<=n}{lcp(suf(i),suf(j))}\)

\(=\frac{(n-1)*(1+n)*n}{2}-2*\displaystyle \sum^{}_{1<=i<j<=n}{lcp(suf(i),suf(j))}\)

注意前者的\(i\)与\(j\)是转换了一下的,为了后面化简顺利。(也就是说前面的\(i\)与\(j\)与后面的\(i\)与\(j\)不是对应一样的)

我们就可以考虑后面的环节。因为暴枚的时间是\(n\)方,我们肯定过不去。所以只有用一个小一点的枚举。

可以利用\(h\)数组。因为两个后缀的\(lcp\)是排名在它们之间的串的\(h\)值的\(min\)。反过来思考,我们只要计算出以\(i\)为中心的\(h\)值比\(h[i]\)大的连续区间的长度就行了。我们可以用单调栈维护。

统计出两边的个数后我们相乘就能得到\(lcp\)为\(h[i]\)的区间个数了。

注意处理最后仍未被弹出栈的元素。

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
using namespace std;

const int N = 5e5 + 3;

long long ans;
int cnt, l[N], r[N], s[N], lg[N], T, rmq[N][20], n, m, h[N], tax[N], SA[N], tp[N], rk[N], a[N];

int read() {
    int x = 0, f = 1; char S;
    while((S = getchar()) > '9' || S < '0') {
        if(S == '-') f = -1;
        if(S == EOF) exit(0);
    }
    while(S <= '9' && S >= '0') {
        x = (x << 1) + (x << 3) + (S ^ 48);
        S = getchar();
    }
    return x * f;
}

bool cmp(const int x, const int y, const int d) {return tp[x] == tp[y] && tp[x + d] == tp[y + d];}

void Sort() {
    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];
}

void Swap(int &x, int &y) {
    x ^= y ^= x ^= y;
}

int Min(const int x, const int y) {
    if(x < y) return x;
    return y;
}

void Suffix() {
    for(int i = 1; i <= n; ++ i) rk[i] = a[i], tp[i] = i;
    m = 122; Sort();
    for(int w = 1, p = 1, i; p < n; m = p, w <<= 1) {
        for(p = 0, i = n - w + 1; i <= n; ++ i) tp[++ p] = i;
        for(i = 1; i <= n; ++ i) if(SA[i] > w) tp[++ p] = SA[i] - w;
        Sort(); swap(rk, tp); rk[SA[1]] = p = 1;
        for(i = 2; i <= n; ++ i) rk[SA[i]] = cmp(SA[i], SA[i - 1], w) ? p : ++ p;
    }
    int j, k = 0;
    for(int i = 1; i <= n; h[rk[i ++]] = k)
        for(k = k ? k - 1 : k, j = SA[rk[i] - 1]; a[i + k] == a[j + k]; ++ k);
    for(int i = 1; i <= n; ++ i) rmq[i][0] = h[i];
    for(int j = 1; (1 << j) <= n; ++ j)
        for(int i = 1; i + (1 << j) - 1 <= n; ++ i)
            rmq[i][j] = Min(rmq[i][j - 1], rmq[i + (1 << j - 1)][j - 1]);
}

int lcp(const int x, const int y) {
    int l = rk[x], r = rk[y];
    if(l > r) Swap(l, r);
    int t = lg[r - l];
    return Min(rmq[l + 1][t], rmq[r - (1 << t) + 1][t]);
}

int main() {
    char ch[N];
    scanf("%s", ch); n = strlen(ch);
    for(int i = 1; i <= n; ++ i) a[i] = ch[i - 1];
    Suffix();
    ans = 1ll * n * (n - 1) * (n + 1) / 2;
    s[cnt = 1] = 1;
    for(int i = 2; i <= n; ++ i) {
        while(cnt && h[s[cnt]] > h[i]) r[s[cnt --]] = i;
        l[i] = s[cnt];
        s[++ cnt] = i;
    }
    while(cnt) r[s[cnt --]] = n + 1;
    for(int i = 2; i <= n; ++ i)
        ans -= 2ll * (i - l[i]) * (r[i] - i) * h[i];
    printf("%lld\n", ans);
    return 0;
}

原文地址:https://www.cnblogs.com/AWhiteWall/p/12394034.html

时间: 2024-07-31 19:09:23

【AHOI 2013】差异的相关文章

BZOJ 3238 AHOI 2013 差异 后缀数组+单调栈

题目大意: 思路:一看各种后缀那就是后缀数组没跑了. 求出sa,height之后就可以乱搞了.对于height数组中的一个值,height[i]来说,这个值能够作为lcp值的作用域只在左边第一个比他小的位置到右边第一个比他小的位置.这个东西很明显可以倍增RMQ+二分/单调栈. 之后就是数学题了 Σlen[Ti] + len[Tj] = (len + 1) * len * (len - 1),之后吧所有求出来的Σ2 * lcp(Ti,Tj)减掉就是答案. 记得答案开long long CODE:

[BZOJ 3238] [AHOI 2013] 差异

题目链接:BZOJ - 3238 题目分析 显然,这道题就是求任意两个后缀之间的LCP的和,这与后缀数组的联系十分明显. 求出后缀数组后,求出字典序相邻两个后缀的LCP,即 Height 数组. 那么我们可以用这个 Height 数组求出所有后缀之间 LCP 的和. 我们用 f[i] 表示字典序第 i 的后缀与字典序在 i 之后的所有后缀的 LCP 的和. 我们知道,两个后缀的 LCP 为 Height 数组中这两个后缀之间的最小值. 我们从最后向前推 i ,用一个单调栈维护后面的 Height

BZOJ 3238 AHOI 2013 差异 后缀树

题目大意:求所有后缀长度减去LCP长度的二倍. 思路:之前用后缀数组写过,但是做法并不是很直观.现在学了后缀树再来写一次,这次思路就很清晰了. 首先我们把字符串按照倒序插入到后缀树中.形成的后缀树有一个很好的性质,连个后缀节点的LCA就是这两个后缀的LCP的位置,LCA的len值自然就是两个后缀的LCP. 建好树之后,进行一次树形DP,统计出来每两个后缀的LCP长度,计入总答案. 这东西以后不用指针写了,简直DT死了.. CODE: #include <cstdio> #include <

[AHOI 2013] 差异

[题目链接] https://www.lydsy.com/JudgeOnline/problem.php?id=3238 [算法] 首先 , LCP(Ti , Tj) = min{ height[rank[Ti] + 1] , height[rank[Ti] + 2] , ... , height[rank[Tj]] }          显然 , 我们只要计算后缀两两之间的LCP之和即可 对于一个右端点R , 随着L的减小 , min{height[L] , height[L + 1] ..

「AHOI 2013」差异 「SA」「单调栈」

首先明确题目要我们求什么.看到后面的 \(LCP(T_i, T_j)\) 很容易用后缀数组将其转化成 \(\min_{rk[i] < k \leq rk[j]}{height[k]}\).\((若rk[i] < rk[j])\) 考虑计算每个位置的h作为min出现的次数.很明显这个东西可以用单调栈一步求出来.那么就转为计算 \(\sum_{p = l} ^ {i} \sum_{p = i} ^ {r} (n - sa[i - 1] + 1) + (n - sa[i] + 1)\). 然后大家只

BZOJ 3236 AHOI 2013 作业 莫队算法

题目大意:给出一些数,问在一个区间中不同的数值有多少种,和在一个区间中不同的数值有多少个. 思路:由于没有修改,所以就想到了莫队算法.然后我写了5K+的曼哈顿距离最小生成树,然后果断T了.(100s的时限啊,刷status都要刷疯了..,结果最后加了手写读入也没能A).后来果断放弃,写了分块版的莫队算法.84sAC...这题卡的..貌似莫队并不是正解. 其实用分块来写莫队就很简单了.只需要将所有询问的区间排序,左端点所在块作为第一键值,右端点作为第二季键值排序,之后就可以转移了.理论上来时还是曼

NOI 2015 滞后赛解题报告

报同步赛的时候出了些意外.于是仅仅能做一做"滞后赛"了2333 DAY1 T1离线+离散化搞,对于相等的部分直接并查集,不等部分查看是否在同一并查集中就可以,code: #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int t,n; int father[200001]; struct hp{ i

通达OA 2013版和2013增强版两个版本开发的一些差异

最近在用这两个版本做些东西的时候,发现两个版本里面确实有些差异,记录分享一下: 通达2013版里面有CRM这个模块,而在2013增强版缺没有了,而且安装之后的数据库里也没有这些数据表. 在引用的文件里,2013版里的auth.php,在增强版里叫auth.inc.php,如果两个版本切换开发的话,容易出现找不到文件的情况.类似的情况还有header.inc.php这个文件,找不到的话你就看看有没有这种情况.

[.net 面向对象程序设计深入](1)UML——在Visual Studio 2013/2015中设计UML类图

[.net 面向对象程序设计深入](1)UML——在Visual Studio 2013/2015中设计UML类图 1.UML简介 Unified Modeling Language (UML)又称统一建模语言或标准建模语言. 简单说就是以图形方式表现模型,根据不同模型进行分类,在UML 2.0中有13种图,以下是他们的主要用途简介: 1.用例图:对系统的使用方式分类. 2.类图:显示类和它们的相互关系. 3.对象图:只显示对象及它们的相互关系. 4.活动图:显示人或对象的活动,其方式类似于流程