后缀数组+单调栈的应用

首先我们研究一下这个表达式,可以发现前半部分与串的情况并没有关系,而只是跟串的长度有关,所以我们先把前半部分算出来:

于是我们只需计算出即可

那么可以发现,对于排名分别为i,j的两个串,他们的lcp应当是:

但是这里的时间复杂度仍然很大

我们换一个角度来思考:如果设,那么我们认为height[k]产生了一个贡献

所以我们可以从每一个height[k]产生了多少贡献的角度来思考,这样就可以把时间复杂度降到O(n)

不难发现,一个k会对一个区间产生贡献的条件就是height[k]是所在区间的最小值

这就可以用单调栈维护了!!

但是要注意,为了防止重复计算,我们对单调栈的两端点的取等条件设成不一样的(即左侧算到第一个height小于等于height[k],右侧算到第一个height小于height[k]的位置)

这样找到每个点向左和向右能延伸的位置lx,rx这样他所占的区间个数就是(i-lx)*(rx-i)

这样去更新就可以了

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#define ll long long
using namespace std;
char s[500005];
int sa[500005];
int rank[500005];
int temprank[500005];
int height[500005];
int has[500005];
int v[500005];
int lx[500005],rx[500005];
int l;
bool be_same(int x,int y,int len)
{
return x+len>l||y+len>l||rank[x]!=rank[y]||rank[x+len]!=rank[y+len];
}
void get_sa()
{
int cnt=0;
for(int i=1;i<=l;i++)v[i]=s[i];
for(int i=1;i<=l;i++)has[v[i]]++;
for(int i=0;i<128;i++)if(has[i])temprank[i]=++cnt;
for(int i=1;i<128;i++)has[i]+=has[i-1];
for(int i=1;i<=l;i++)
{
rank[i]=temprank[v[i]];
sa[has[v[i]]--]=i;
}
for(int k=1;cnt!=l;k<<=1)
{
cnt=0;
for(int i=1;i<=l;i++)has[i]=0;
for(int i=1;i<=l;i++)has[rank[i]]++;
for(int i=1;i<=l;i++)has[i]+=has[i-1];
for(int i=l;i;i--)if(sa[i]>k)temprank[sa[i]-k]=has[rank[sa[i]-k]]--;
for(int i=1;i<=k;i++)temprank[l-i+1]=has[rank[l-i+1]]--;
for(int i=1;i<=l;i++)sa[temprank[i]]=i;
for(int i=1;i<=l;i++)temprank[sa[i]]=be_same(sa[i],sa[i-1],k)?++cnt:cnt;
for(int i=1;i<=l;i++)rank[i]=temprank[i];
}
for(int i=1;i<=l;i++)
{
if(rank[i]==1)continue;
int j=max(1,height[rank[i-1]]-1);
while(s[i+j-1]==s[sa[rank[i]-1]+j-1])height[rank[i]]=j++;
}
}
void init()
{
height[0]=height[l+1]=-0x3f3f3f3f;
ll ret=0;
for(int i=1;i<=l;i++)lx[i]=i-1,rx[i]=i+1;
for(int i=2;i<=l;i++)while(height[lx[i]]>height[i])lx[i]=lx[lx[i]];
for(int i=l;i>=2;i--)while(height[rx[i]]>=height[i])rx[i]=rx[rx[i]];
for(int i=2;i<=l;i++)ret+=2*height[i]*(ll)((ll)(rx[i]-i)*(ll)(i-lx[i]));
ll ans=1ll*(l-1)*l/2ll*(l+1);
printf("%lld\n",ans-ret);
}
int main()
{
scanf("%s",s+1);
l=strlen(s+1);
get_sa();
init();
return 0;
}

bzoj 3238的更多相关文章

  1. [BZOJ 3238] [AHOI 2013] 差异 【后缀数组 + 单调栈】

    题目链接:BZOJ - 3238 题目分析 显然,这道题就是求任意两个后缀之间的LCP的和,这与后缀数组的联系十分明显. 求出后缀数组后,求出字典序相邻两个后缀的LCP,即 Height 数组. 那么 ...

  2. BZOJ 3238 差异

    BZOJ 3238 差异 看这个式子其实就是求任意两个后缀的 $ LCP $ 长度和.前面的 $ len(T_i)+len(T_j) $ 求和其实就是 $ n(n-1)(n+1)/2 $ ,这个是很好 ...

  3. BZOJ 3238: [Ahoi2013]差异 [后缀数组 单调栈]

    3238: [Ahoi2013]差异 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 2326  Solved: 1054[Submit][Status ...

  4. bzoj 3238 Ahoi2013 差异

    3238: [Ahoi2013]差异 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 2357  Solved: 1067[Submit][Status ...

  5. BZOJ 3238: [Ahoi2013]差异 [后缀自动机]

    3238: [Ahoi2013]差异 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 2512  Solved: 1140[Submit][Status ...

  6. ●BZOJ 3238 [Ahoi2013]差异

    题链: http://www.lydsy.com/JudgeOnline/problem.php?id=3238 题解: 后缀数组套路深. 问题转化为求出任意两个后缀的LCP之和 在计算贡献时,各种不 ...

  7. BZOJ 3238 [Ahoi2013]差异(后缀自动机)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3238 [题目大意] 给出一个串,设T[i]表示从第i位开始的后缀, 求sum(len( ...

  8. 【BZOJ 3238】 3238: [Ahoi2013]差异(SAM)

    3238: [Ahoi2013]差异 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 3047  Solved: 1375 Description In ...

  9. BZOJ 3238: [Ahoi2013]差异 后缀自动机 树形dp

    http://www.lydsy.com/JudgeOnline/problem.php?id=3238 就算是全局变量,也不要忘记,初始化(吐血). 长得一副lca样,没想到是个树形dp(小丫头还有 ...

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

    3238: [Ahoi2013]差异 Time Limit: 20 Sec  Memory Limit: 512 MB Description Input 一行,一个字符串S Output 一行,一个 ...

随机推荐

  1. Python基础知识3-函数、参数及参数解构

    函数 函数定义.调用 函数参数 函数参数默认参数 函数参数默认值 可变参数 keyword-only参数 可变参数和参数默认值 函数参数 参数解构 练习: #编写一个函数,能够接受至少2个参数 def ...

  2. JSON数组形式字符串转换为List<Map<String,String>>的8种方法

    package com.zkn.newlearn.json; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArr ...

  3. mysql-笔记 json

    1 JSON 列不能有non-NULL 默认值 2 JSON值:数组:["abc",10,null,true,false] 可嵌套 对象:{"k1":" ...

  4. 洛谷P2634 聪明可可

    还是点分治 树上问题真有趣ovo,这道题统计模3为0的距离,可以把重心的子树分开统计,也可以一次性统计,然后容斥原理减掉重复的.. 其他的过程就是点分治的板子啦. #include <bits/ ...

  5. 并发容器学习—ConcurrentSkipListMap与ConcurrentSkipListSet 原

    一.ConcurrentSkipListMap并发容器 1.ConcurrentSkipListMap的底层数据结构     要学习ConcurrentSkipListMap,首先要知道什么是跳表或跳 ...

  6. <Android基础>(四) Fragment Part 1

    Fragment 1)Fragment的简单用法 2)动态添加Fragment 3)在Fragment中模拟返回栈 4)Fragment和活动之间通信 第四章 Fragment Fragment是一种 ...

  7. vim命令编辑Host文件

    用vim命令打开Host文件 [root@localhost /]# vim /etc/hosts 按"i"键,进入编辑模式按"Esc"键,退出编辑模式 按&q ...

  8. Win10激活工具

    W10数字许可激活C#版v2.8.0百度网盘下载地址:https://pan.baidu.com/s/1TD0PVxIfB2NTarAuP9NJbQ直接下载地址:FTP://A@OS.X6X8.COM ...

  9. 关于QQ农场牧场等曾经流行的游戏的一些见解

    大概在上上周,我偶然间打开QQ空间玩了一会QQ农牧场,玩完之后我在想,在那个年代他们为什么那么红? 我觉得可能有以下几点: 1:凭借着QQ海量的用户,可以迅速推广 2:迎合了人们爱占小便宜的心理,不过 ...

  10. Mac tree命令的使用,输出目录树结构

    安装: brew install tree 常用命令: tree --help:  查看帮助信息 tree -d:  只显示文件夹 tree -D:  显示文件的最后修改时间   tree -I no ...