题目地址:P4070 [SDOI2016]生成魔咒

相信看到题目之后很多人跟我的思路是一样的——

肯定要用 SAP3809 【模板】后缀排序

肯定要会求本质不同的子串个数P2408 不同子串个数

然后?就不会了......

瓶颈在哪儿?

你会发现每往后添加一个字符,整个 sa 数组只会插入一个数,要维护不难

但是 height无规律变化,这就导致无法高效维护

怎么办呢?

倒置字符串

我们将整个字符串倒置过来

显然本质不同的子串个数不会变化

而每往前添加一个字符串, height 的变化是 \(O(1)\) 的

那么,问题就变得简单很多了

具体实现请看代码注释

#include <bits/stdc++.h>
#define ll long long
#define si set<int>::iterator
using namespace std;
const int N = 1e5 + 6;
int n, m, a[N], b[N];
int sa[N], rk[N], tp[N], tx[N], he[N], st[N][20];
ll ans;
set<int> s;

inline void tsort() {//基数排序
    for (int i = 1; i <= m; i++) tx[i] = 0;
    for (int i = 1; i <= n; i++) ++tx[rk[i]];
    for (int i = 1; i <= m; i++) tx[i] += tx[i-1];
    for (int i = n; i; i--) sa[tx[rk[tp[i]]]--] = tp[i];
}

inline bool pd(int i, int w) {
    return tp[sa[i-1]] == tp[sa[i]] && tp[sa[i-1]+w] == tp[sa[i]+w];
}

inline void SA() {//后缀数组板子
    for (int i = 1; i <= n; i++) {
        rk[i] = a[i] = lower_bound(b + 1, b + m + 1, a[i]) - b;
        tp[i] = i;
    }
    tsort();
    for (int w = 1, p = 0; p < n; m = p, w <<= 1) {
        p = 0;
        for (int i = 1; i <= w; i++) tp[++p] = n - w + i;
        for (int i = 1; i <= n; i++)
            if (sa[i] > w) tp[++p] = sa[i] - w;
        tsort();
        swap(rk, tp);
        rk[sa[1]] = p = 1;
        for (int i = 2; i <= n; i++)
            rk[sa[i]] = pd(i, w) ? p : ++p;
    }
    int p = 0;
    for (int i = 1; i <= n; i++) {
        if (p) --p;
        int j = sa[rk[i]-1];
        while (a[i+p] == a[j+p]) ++p;
        he[rk[i]] = p;
    }
}

inline void ST() {//构造ST表
    for (int i = 1; i <= n; i++) st[i][0] = he[i];
    int w = log(n) / log(2);
    for (int k = 1; k <= w; k++)
        for (int i = 1; i <= n; i++) {
            if (i + (1 << k) > n + 1) break;
            st[i][k] = min(st[i][k-1], st[i+(1<<(k-1))][k-1]);
        }
}

inline int get(int l, int r) {//求l~r之间的最小值(即l-1与r的lcp)
    int k = log(r - l + 1) / log(2);
    return min(st[l][k], st[r-(1<<k)+1][k]);
}

int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        b[i] = a[i];
    }
    //离散化
    sort(b + 1, b + n + 1);
    m = unique(b + 1, b + n + 1) - (b + 1);
    reverse(a + 1, a + n + 1);//倒置字符串
    SA();//求sa,rk,height数组
    ST();//ST表
    for (int i = n; i; i--) {//倒序考虑
        s.insert(rk[i]);//以rk为关键字插入set
        si it = s.find(rk[i]);//找到插入的位置
        int k = 0;//存最长lcp
        if (it != s.begin()) {//找前驱,注意特判
            int p = *(--it);
            k = get(p + 1, rk[i]);
            ++it;
        }
        ++it;
        if (it != s.end()) {//找后继,注意特判
            int p = *it;
            k = max(k, get(rk[i] + 1, p));
        }
        ans += n + 1 - i - k;//加上新生成的子串
        printf("%lld\n", ans);
    }
    return 0;
}

P4070 [SDOI2016]生成魔咒的更多相关文章

  1. bzoj4516 / P4070 [SDOI2016]生成魔咒

    P4070 [SDOI2016]生成魔咒 后缀自动机 每插入一个字符,对答案的贡献为$len[last]-len[fa[last]]$ 插入字符范围过大,所以使用$map$存储. (去掉第35行就是裸 ...

  2. 洛谷 P4070 [SDOI2016]生成魔咒 解题报告

    P4070 [SDOI2016]生成魔咒 题目描述 魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示.例如可以将魔咒字符 \(1\).\(2\) 拼凑起来形成一个魔咒串 \([1,2]\). 一个魔咒 ...

  3. Luogu P4070 [SDOI2016]生成魔咒

    题目链接 \(Click\) \(Here\) 其实是看后缀数组资料看到这个题目的,但是一眼反应显然后缀自动机,每次维护添加节点后的答案贡献即可,唯一不友好的一点是需要平衡树维护,这里因为复杂度不卡而 ...

  4. [洛谷P4070][SDOI2016]生成魔咒

    题目大意:有一个字符串,每次在末尾加入一个字符,问当前共有多少个本质不同的字串 题解:$SAM$,就是问插入这个字符后,多了多少个字串,就是当前这个点的$Right$数组大小. 卡点:无 C++ Co ...

  5. BZOJ4516: [Sdoi2016]生成魔咒 后缀自动机

    #include<iostream> #include<cstdio> #include<cstring> #include<queue> #inclu ...

  6. BZOJ 4516: [Sdoi2016]生成魔咒 [后缀自动机]

    4516: [Sdoi2016]生成魔咒 题意:询问一个字符串每个前缀有多少不同的子串 做了一下SDOI2016R1D2,题好水啊随便AK 强行开map上SAM 每个状态的贡献就是\(Max(s)-M ...

  7. BZOJ_4516_[Sdoi2016]生成魔咒_后缀数组+ST表+splay

    BZOJ_4516_[Sdoi2016]生成魔咒_后缀数组+ST表+splay Description 魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示.例如可以将魔咒字符 1.2 拼凑起来形成一个魔 ...

  8. 【LG4070】[SDOI2016]生成魔咒

    [LG4070][SDOI2016]生成魔咒 题面 洛谷 题解 如果我们不用在线输的话,那么答案就是对于所有状态\(i\) \[ \sum (i.len-i.fa.len) \] 现在我们需要在线询问 ...

  9. [Sdoi2016]生成魔咒[SAM or SA]

    4516: [Sdoi2016]生成魔咒 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1017  Solved: 569[Submit][Statu ...

随机推荐

  1. 把菜单栏变成万能工具箱,让你的 Mac 更酷炫

    文章来源:知乎 文章收录于:风云社区 www.scoee.com,提供上千款各类mac软件下载 为了彰显存在感,各路 Mac 应用都喜欢在菜单栏上安置一个图标:其中有的只是用来召唤主界面,也有一些应用 ...

  2. Storm中重要对象的生命周期

    Spout方法调用顺势 declareOutputFields()(调用一次) open() (调用一次) activate() (调用一次) nextTuple() (循环调用 ) deactiva ...

  3. jenkins 基本配置

    修改jenkins使用用户和启动端口 //yum 安装 cat /etc/sysconfig/jenkins 安装完后安装maven插件 设置jenkins项目存放目录 系统管理---->系统设 ...

  4. 在Mac OS环境下安装MySQL服务

    在Mac OS环境下安装MySQL服务 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 我之前介绍过window环境下安装mysql服务,以及在Linux环境下安装mysql服务,今 ...

  5. 面向对象【day08】:异常处理(六)

    本节内容 1.概述 2.异常梳理 3.异常梳理流程图 4.异常大全 5.自定义异常 一.概述 异常处理是当程序出错了,但是我们又不想让用户看到这个错误,而且我在写程序的时候已经预料到了它可以出现这样的 ...

  6. System.Web.Optimization 合并压缩技术的使用

    捆绑和压缩原理是:将多个css文件动态合并和压缩为一个css文件.多个js文件动态合并和压缩为一个js文件,如此达到减少浏览器对服务器资源文件的请求数量.缩小资源文件的尺寸来提高页面反应速度的目的.A ...

  7. spring boot集成redis的血泪史

    首先说明环境不是我搭建的,然后因项目需要添加redis的时候,麻烦来了.springboot 用的是1.5.9因为以前弄过redis,所以直接拿过来,麻烦了首先是莫名的错误,连项目都启动不了.但是最后 ...

  8. HDU - 5119 Happy Matt Friends(dp)

    题目链接 题意:n个数,你可以从中选一些数,也可以不选,选出来的元素的异或和大于m时,则称满足情况.问满足情况的方案数为多少. 分析:本来以为是用什么特殊的数据结构来操作,没想到是dp,还好队友很强. ...

  9. Mac 开发使用中的小技巧收集

    1. mac 下ssh连接到 linux 服务器管理,同putty,无需第三方 Mac 下打开终端,输入: ssh 登录用户名@ip地址 如: ssh root@142.138.1.89 如有询问是否 ...

  10. 微信小程序开发(6) SSL证书及HTTPS服务器

    1. 域名 在万网购买,略 2. 云服务器 阿里云购买,略 3. 安装lnmp 使用lnmp.org程序,略 4. 申请证书 阿里云-管理控制台-安全(云盾)-证书服务-购买证书证书类型: 免费型DV ...