【BZOJ3238】[AHOI2013]差异

题面

给定字符串\(S\),令\(T_i\)表示以它从第\(i\)个字符开始的后缀。求

\[\sum_{1\leq i<j\leq n}len(T_i)+len(T_j)-2*lcp(T_i,T_j)
\]

其中\(len(a)\)表示串\(a\)的长度,\(lcp(a,b)\)表示串\(a,b\)的最长公共前缀

题解

把这个式子看作两边分开求:

Part1:

\[\sum_{1\leq i<j\leq n}len(T_i)+len(T_j)\\
\Leftrightarrow\sum_{i=1}^{n-1}\sum_{j=i+1}^ni+j\\
=\sum_{i=1}^{n-1}i*(n-i)+\frac{(i+1+n)*(n-i)}2
\]

其实现在你就可以\(O(n)\)地求了,但是因为我出(kan)于(le)美(ti)观(jie)

发现它其实可以化成这样:

\[\frac {(n-1)*n*(n+1)}2
\]

Part2:

一看到后缀当然是\(sa\)啦

由后缀数组的性质,排名为分别为\(i,j\)的后缀,\(lcp_{i,j}=\min\limits_{k=i+1}^jheight_k\)

我们将所有高度数组排成一排,

假设中间的第\(i\)个数是\(l-r\)中最小的

则它的贡献就是\((i-l+1)*(r-i+1)\)

我们处理出来对\(i\)所有的\(l,r\)是不是就做出来了呢

这不就是一个单调栈的经典应用吗?

而这个题目中因为一些细节问题我的\(l\)表示小于\(i\)的第一个,\(r\)表示小于等于\(i\)的第一个

详见代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
inline int gi() {
register int data = 0, w = 1;
register char ch = 0;
while (!isdigit(ch) && ch != '-') ch = getchar();
if (ch == '-') w = -1, ch = getchar();
while (isdigit(ch)) data = 10 * data + ch - '0', ch = getchar();
return w * data;
}
const int MAX_N = 5e5 + 5;
int N; char a[MAX_N];
int sa[MAX_N], lcp[MAX_N], rnk[MAX_N];
void GetSA() {
#define cmp(i, j, k) (y[i] == y[j] && y[i + k] == y[j + k])
static int x[MAX_N], y[MAX_N], bln[MAX_N];
int M = 122;
for (int i = 1; i <= N; i++) bln[x[i] = a[i]]++;
for (int i = 1; i <= M; i++) bln[i] += bln[i - 1];
for (int i = N; i >= 1; i--) sa[bln[x[i]]--] = i;
for (int k = 1; k <= N; k <<= 1) {
int p = 0;
for (int i = 0; i <= M; i++) y[i] = 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 = 0; i <= M; i++) bln[i] = 0;
for (int i = 1; i <= N; i++) bln[x[y[i]]]++;
for (int i = 1; i <= M; i++) bln[i] += bln[i - 1];
for (int i = N; i >= 1; i--) sa[bln[x[y[i]]]--] = y[i];
swap(x, y); x[sa[1]] = p = 1;
for (int i = 2; i <= N; i++) x[sa[i]] = cmp(sa[i], sa[i - 1], k) ? p : ++p;
if (p >= N) break;
M = p;
}
}
void GetLcp() {
for (int i = 1; i <= N; i++) rnk[sa[i]] = i;
for (int i = 1, j = 0; i <= N; i++) {
if (j) --j;
while (a[i + j] == a[sa[rnk[i] - 1] + j]) ++j;
lcp[rnk[i]] = j;
}
}
typedef long long ll;
int lp[MAX_N], rp[MAX_N], stk[MAX_N], top;
int main () {
scanf("%s", a + 1); N = strlen(a + 1);
GetSA(); GetLcp();
ll ans = 0;
//for (int i = 1; i < N; i++) ans += 1ll * i * (N - i) + 1ll * (i + 1 + N) * (N - i) / 2ll;
ans = 1ll * N * (N + 1) * (N - 1) / 2ll;
stk[0] = 1;
for (int i = 2; i <= N; i++) {
while (top > 0 && lcp[stk[top]] >= lcp[i]) --top;
lp[i] = i - stk[top], stk[++top] = i;
}
top = 0, stk[0] = N + 1;
for (int i = N; i >= 2; i--) {
while (top > 0 && lcp[stk[top]] > lcp[i]) --top;
rp[i] = stk[top] - i, stk[++top] = i;
}
for (int i = 2; i <= N; i++) ans -= 2ll * lcp[i] * lp[i] * rp[i];
printf("%lld\n", ans);
return 0;
}

【BZOJ3238】[AHOI2013]差异的更多相关文章

  1. BZOJ3238 [Ahoi2013]差异 【SAM or SA】

    BZOJ3238 [Ahoi2013]差异 给定一个串,问其任意两个后缀的最长公共前缀长度的和 1.又是后缀,又是\(lcp\),很显然直接拿\(SA\)的\(height\)数组搞就好了,配合一下单 ...

  2. bzoj3238 [Ahoi2013]差异 后缀数组+单调栈

    [bzoj3238][Ahoi2013]差异 Description Input 一行,一个字符串S Output 一行,一个整数,表示所求值 Sample Input cacao Sample Ou ...

  3. [bzoj3238][Ahoi2013]差异_后缀数组_单调栈

    差异 bzoj-3238 Ahoi-2013 题目大意:求任意两个后缀之间的$LCP$的和. 注释:$1\le length \le 5\cdot 10^5$. 想法: 两个后缀之间的$LCP$和显然 ...

  4. [BZOJ3238][AHOI2013]差异(后缀数组)

    求和式的前两项可以直接算,问题是对于每对i,j计算LCP. 一个比较显然的性质是,LCP(i,j)是h[rk[i]+1~rk[j]]中的最小值. 从h的每个元素角度考虑,就是对每个h计算有多少对i,j ...

  5. [BZOJ3238][Ahoi2013]差异解题报告|后缀数组

    Description 先分析一下题目,我们显然可以直接算出sigma(len[Ti]+len[Tj])的值=(n-1)*n*(n+1)/2 接着就要去算这个字符串中所有后缀的两两最长公共前缀总和 首 ...

  6. BZOJ3238 [Ahoi2013]差异 【后缀数组 + 单调栈】

    题目链接 BZOJ3238 题解 简单题 经典后缀数组 + 单调栈套路,求所有后缀\(lcp\) #include<iostream> #include<cstdio> #in ...

  7. BZOJ3238: [Ahoi2013]差异 (后缀自动机)

    Description Input 一行,一个字符串S Output 一行,一个整数,表示所求值 Sample Input cacao Sample Output 54 HINT 2<=N< ...

  8. BZOJ3238 [Ahoi2013]差异

    首先把后缀数组和height数组都搞出来... 然后用两个单调栈维护$[l, r]$表示对于一个点$x$,满足$height[x] \le height[l..x] \ \&\&\   ...

  9. bzoj千题计划314:bzoj3238: [Ahoi2013]差异(后缀数组+st表+单调栈)

    https://www.lydsy.com/JudgeOnline/problem.php?id=3238 跟 bzoj3879 差不多 #include<cstdio> #include ...

  10. 2018.12.21 bzoj3238: [Ahoi2013]差异(后缀自动机)

    传送门 后缀自动机好题. 题意: 做法:samsamsam 废话 考虑翻转字串,这样后缀的最长公共前缀等于前缀的最长公共后缀. 然后想到parentparentparent树上面两个串的最长公共后缀跟 ...

随机推荐

  1. 如何修改jenkins的启动用户?

    如何修改运行jenkins进程的linux帐号? 1.找的jenkins的配置文件,一般是/etc/sysconfig/jenkins 2.修改下面的参数为相应的用户,比如JENKINS_USER=& ...

  2. 【JVM】浅谈双亲委派和破坏双亲委派

    一.前言 笔者曾经阅读过周志明的<深入理解Java虚拟机>这本书,阅读完后自以为对jvm有了一定的了解,然而当真正碰到问题的时候,才发现自己读的有多粗糙,也体会到只有实践才能加深理解,正应 ...

  3. php 使用PHPExcel 导出数据为Excel

    <?php require_once 'PHPExcel/Classes/PHPExcel.php'; /** * 导出数据为Excel * @param array $fieldArr 标题数 ...

  4. ecplise导入工程出现乱码的解决方案

    eclipse之所以会出现乱码问题是因为eclipse编辑器选择的编码规则是可变的.一般默认都是UTF-8或者GBK,当从外部导入的一个工程时,如果该工程的编码方式与eclipse中设置的编码方式不同 ...

  5. Halcon的一维条码解码步骤和解码技巧

    一.图像预处理和条码增强 对比度太低:scale_image(或使用外部程序scale_image_range),增强图像的对比度. 图像模糊:emphasize锐化图像,使条码看起来更清晰. 深色背 ...

  6. SNP问题大集锦

    SNP问题大集锦 [2017-01-19]       最近小编对基因检测很感兴趣,也跟风去测了一下,这一测不要紧,吓得小编几天没睡着觉,这不,检测报告上称小编的减肥能力弱,虽然小编一家都是胖子,唯有 ...

  7. javax.mail 发送邮件 , greenMail 测试 , spring

    发送邮件接口: package com.manxu.archetype.account.email; import javax.security.auth.login.AccountException ...

  8. 关闭文件流--fclose,

    头文件:#include<stdio.h> 函数原型:int fclose(FILE *fp) 参数说明:fp将被关闭的文件指针 返回值:成功返回0,失败返回EOF宏.

  9. python协程函数、递归、匿名函数与内置函数使用、模块与包

    目录: 协程函数(yield生成器用法二) 面向过程编程 递归 匿名函数与内置函数的使用 模块 包 常用标准模块之re(正则表达式) 一.协程函数(yield生成器用法二) 1.生成器的语句形式 a. ...

  10. R 语言安装问题集锦

    R安装问题R CMD INSTALL -l /data1/jhh/envirment/R/R_lib /data1/jhh/software/qvalue_2.10.0.tar.gz 问题1 :con ...