题目背景

ZRQ 成功从坍塌的洞穴中逃了出来。终于,他看到了要研究的矿石。他想挑一些带回去完成任务。

题目来源:Zhang_RQ哦对了 \(ZRQ\) 就他,嗯

题目描述

ZRQ 发现这里有 \(N\) 块排成一排的矿石。

他用一个小写字母来表示每块矿石,他还发现每块矿石有一个重要度 \(V_i\)。

ZRQ 想采集一段连续的矿石回研究所。

他非常严格,被采集的一段矿石必须满足小写字母的字典序降序排名等于这段矿石的重要度和。

这里多个出现在不同位置的本质相同串的字典序排名相同。

比如说字母串为 aa,那么第一个 a 的排名和第二个 a 的排名相同,都是 2(第 1aa)。

ZRQ 问你,在原串中有哪些不同的子串可以被采集?

这里子串不同定义为出现位置不同,也就是说本质相同的子串出现在不同位置都要计算一次(当然重要度和等于排名是前提)。

比如共有 \(4\) 块矿石,小写字母串为 abcd,重要度各为 10 0 1 1

我们把所有的子串按照字典序从大到小排名:1:d 2:cd 3:c 4:bcd 5:bc 6:b 7:abcd 8:abc 9:ab 10:a

那么串 d 的排名为 \(1\)(第一大),重要度和为 \(1\),可以被采集。

cd 的排名为 \(2\),重要度和为 \(2\),可以被采集。

a 的排名为 \(10\),重要度和为 \(10\),可以被采集。

其他串则不满足这个条件,故有三个串可以被采集。

输入格式

第一行一个长度为 \(N\) 由小写字母组成的字符串,每个字符代表一个矿石。

第二行 \(N\) 个整数,表示 \(V_i\)。

输出格式

一行一个整数,表示能被采集的子串个数 \(S\)。

接下来 \(S\) 行每行两个整数 \(L,R\),分别表示每个可采集子串的左端点与右端点,按照左端点升序为第一关键字,右端点升序为第二关键字排序。

输入输出样例

输入 #1

abcd
10 0 1 1

输出 #1

3
1 1
3 4
4 4

输入 #2

aaaa
1 1 1 1

输出 #2

0

输入 #3

aaa
1 1 1

输出 #3

2
1 2
2 3

输入 #4

aaa
1 1 2

输出 #4

1
1 2

说明/提示

共 \(10\) 个测试点,每个点 \(10\) 分,计 \(100\) 分。

对于所有测试点,有 \(N\leq 10^5\),\(0 \le V_i \le 1000\)。保证每个点可被采集的子串不超过 \(10^5\) 个。

样例#1解释放在题面里了。

样例#2解释:

每个子串都不满足条件。

a 的排名是 \(4\),重要度和都是 \(1\)。

aa 的排名是 \(3\),重要度和都是 \(2\)。

aaa 的排名是 \(2\),重要度和都是 \(3\)。

aaaa 的排名是 \(1\),重要度和都是 \(4\)。

样例 #3解释:

a 的排名是 \(3\),重要度和都是 \(1\)。

aa 的排名是 \(2\),重要度和都是 \(2\),共有两个串aa,位置分别为 \(1 \sim 2\) 和 \(2 \sim3\)。

aaa 的排名是 \(1\),重要度和都是 \(3\)。

样例 #4解释:

可以发现,串 \(2 \sim 3\)(第二个 aa)不满足条件了。它的排名还是 \(2\) 不变,但是重要度和为 \(3\)。

分析

要求出所有排名等于重要度的子串并输出方案

首先可以证明这样的子串不会超过 \(n\) 个

因为假如我们固定了左端点,那么随着右端点的增大,重要度不会变小,但是排名会变小

因此对于一个确定的左端点,最多只会有一个右端点满足条件

又因为重要度和排名都是单调的,所以只需要固定左端点,二分合法的右端点就可以了

考虑如何快速求出一个子串的重要度和排名

重要度可以用前缀和 \(O(1)\) 查询

排名可以用后缀数组求出

在计算一个字符串的本质不同的子串时,我们会用到一个式子

\(\sum_{i=1}^nn-sa[i]+1-height[i]\)

一个很有用的性质就是这样求出的本质不同字串是按顺序的

所以对于一个子串,只需要算出它的前面有多少本质不同的子串即可

先用一个数组记录一下这个式子的前缀和,然后分情况讨论

设 \(fir[i]\) 为 \(i\) 号后缀的排名

如果 \(len \geq height[fir[l]]\),那么排名为 \(sum_{fir[l]}-(n-r)\)

否则向前二分找到一个位置 \(pos\),使得它恰好满足 \(len \geq height[pos]\),那么排名为 \((sum_{pos}-(n-sa[pos]-len+1))\)

后面减去的那一部分是左端点相同但是长度比当前子串长的

因为排名是按照字典序从大到小来的,所以还要拿总的本质不同的子串减去求出的结果

时间复杂度 \(O(nlog^2n)\)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#define rg register
inline int read(){
rg int x=0,fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
const int maxn=1e5+5;
int sa[maxn],fir[maxn],sec[maxn],tax[maxn],n,heig[maxn],lg[maxn],mmin[maxn][20],m;
int a[maxn],sum1[maxn],sta1[maxn],sta2[maxn],tp;
char s[maxn];
long long tot,sum2[maxn];
void Qsort(){
for(rg int i=0;i<=m;i++) tax[i]=0;
for(rg int i=1;i<=n;i++) tax[fir[i]]++;
for(rg int i=1;i<=m;i++) tax[i]+=tax[i-1];
for(rg int i=n;i>=1;i--) sa[tax[fir[sec[i]]]--]=sec[i];
}
void getsa(){
m=1e5;
for(rg int i=1;i<=n;i++) fir[i]=s[i],sec[i]=i;
Qsort();
for(rg int len=1,p=0;p<n;len<<=1,m=p){
p=0;
for(rg int i=n-len+1;i<=n;i++) sec[++p]=i;
for(rg int i=1;i<=n;i++) if(sa[i]>len) sec[++p]=sa[i]-len;
Qsort();
memcpy(sec,fir,sizeof(sec));
fir[sa[1]]=p=1;
for(rg int i=2;i<=n;i++) fir[sa[i]]=(sec[sa[i]]==sec[sa[i-1]] && sec[sa[i]+len]==sec[sa[i-1]+len])?p:++p;
}
}
void getheight(){
rg int j,k=0;
for(rg int i=1;i<=n;i++){
if(k) k--;
j=sa[fir[i]-1];
while(s[i+k]==s[j+k]) k++;
heig[fir[i]]=k;
}
tot=1LL*n*(n+1)/2LL;
for(rg int i=2;i<=n;i++) tot-=heig[i];
for(rg int i=1;i<=n;i++) sum2[i]=sum2[i-1]+n-sa[i]+1-heig[i];
}
void pre(){
for(rg int i=1;i<=n;i++) mmin[i][0]=heig[i];
for(rg int i=2;i<=n;i++) lg[i]=lg[i>>1]+1;
for(rg int j=1;j<=18;j++){
for(rg int i=1;i+(1<<j)-1<=n;i++){
mmin[i][j]=std::min(mmin[i][j-1],mmin[i+(1<<(j-1))][j-1]);
}
}
}
int getans(rg int l,rg int r){
l++;
rg int k=lg[r-l+1];
return std::min(mmin[l][k],mmin[r-(1<<k)+1][k]);
}
int getval(rg int l,rg int r){
return sum1[r]-sum1[l-1];
}
long long getrk(rg int l,rg int r){
rg int len=r-l+1;
if(heig[fir[l]]<=len) return tot-(sum2[fir[l]]-(n-r))+1;
rg int nl=1,nr=fir[l]-1,mids;
while(nl<=nr){
mids=(nl+nr)>>1;
if(getans(mids,fir[l])<=len) nl=mids+1;
else nr=mids-1;
}
return tot-(sum2[nr]-(n-sa[nr]-len+1))+1;
}
void solve(rg int id){
rg int l=id,r=n,mids;
while(l<=r){
mids=(l+r)>>1;
if(getrk(id,mids)==getval(id,mids)){
sta1[++tp]=id,sta2[tp]=mids;
return;
} else if(getrk(id,mids)>getval(id,mids)){
l=mids+1;
} else {
r=mids-1;
}
}
}
int main(){
scanf("%s",s+1);
n=strlen(s+1);
for(rg int i=1;i<=n;i++) a[i]=read();
getsa(),getheight(),pre();
for(rg int i=1;i<=n;i++) sum1[i]=sum1[i-1]+a[i];
for(rg int i=1;i<=n;i++) solve(i);
printf("%d\n",tp);
for(rg int i=1;i<=tp;i++) printf("%d %d\n",sta1[i],sta2[i]);
return 0;
}

洛谷 P4143 采集矿石 后缀数组的更多相关文章

  1. 【刷题】洛谷 P4143 采集矿石

    题目背景 ZRQ成功从坍塌的洞穴中逃了出来.终于,他看到了要研究的矿石.他想挑一些带回去完成任务. 题目来源:Zhang_RQ哦对了ZRQ就他,嗯 题目描述 ZRQ发现这里有 \(N\) 块排成一排的 ...

  2. 洛谷P3763 [TJOI2017]DNA(后缀数组 RMQ)

    题意 题目链接 Sol 这题打死我也不会想到后缀数组的,应该会全程想AC自动机之类的吧 但知道这题能用后缀数组做之后应该就不是那么难了 首先把\(S\)和\(S0\)拼到一起跑,求出Height数组 ...

  3. 洛谷-P3809-后缀排序(后缀数组)

    看了求后缀数组的倍增法之后很快就理解了,但是自己写的倍增法用map排序还是超时了.然后看了两天别人写的模板,题目是通过了,但感觉代码还是半懂半背的.以后多熟悉熟悉吧: 后缀数组 #include &q ...

  4. 洛谷3809 SA模板 后缀数组学习笔记(复习)

    其实SA这个东西很久之前就听过qwq 但是基本已经忘的差不多了 嘤嘤嘤 QWQ感觉自己不是很理解啊 所以写不出来那种博客 QWQ只能安利一些别人的博客了 小老板 真的是讲的非常好 不要在意名字 orz ...

  5. 洛谷P5108 仰望半月的夜空(后缀数组)

    题意 题目链接 Sol warning:下面这个做法只有95分,本地拍了1w+组都没找到错误我表示十分无能为力 我们考虑每个串的排名去更新答案,显然排名为\(1\)的后缀的前缀一定是当前长度的字典序最 ...

  6. [Luogu P4143] 采集矿石 [2018HN省队集训D5T3] 望乡台platform

    [Luogu P4143] 采集矿石 [2018HN省队集训D5T3] 望乡台platform 题意 给定一个小写字母构成的字符串, 每个字符有一个非负权值. 输出所有满足权值和等于这个子串在所有本质 ...

  7. 题解 洛谷 P4143 【采集矿石】

    对于一个固定的左端点,右端点向右移动时,其子串权值和不断增大,字典序降序排名不断减小,因此对于一个左端点,最多存在一个右端点使其满足条件. 所以可以枚举左端点,然后二分右端点的位置,权值和通过前缀和来 ...

  8. 洛谷 P3688 - [ZJOI2017]树状数组(二维线段树+标记永久化)

    题面传送门 首先学过树状数组的应该都知道,将树状数组方向写反等价于前缀和 \(\to\) 后缀和,因此题目中伪代码的区间求和实质上是 \(sum[l-1...n]-sum[r...n]=sum[l-1 ...

  9. 【洛谷P3919】可持久化数组

    题目大意:需要维护一个长度为 N 的数组,支持在历史版本上单点修改和单点查询. 题解:显然,如果直接暴力维护的话会 MLE.因此,采用线段树进行维护,使得空间复杂度由 \(O(mn)\) 降至 \(O ...

随机推荐

  1. WIN7系统没有USB驱动和以太网驱动如何操作

    |  欢迎关注个人公众号  zclinux_note  第一时间获取关于linux使用的技巧.探索Linux的奥秘   | 今天在单位安装了一台win7纯净版,但是安装完成后发现usb没有反应,插上网 ...

  2. python—base64

    今天在写题时,执行脚本又报错了 脚本如下 #! /usr/bin/env python3 # _*_ coding:utf-8 _*_ import base64 # 字典文件路径 dic_file_ ...

  3. SAP 修改数据元素 注意事项

    在修改数据元素的时候,通常要注意一下几点: 1.在修改完数据元素后,如果激活不成功,那么就要通过SE14进入数据库实用程序,在对象名处输入数据元素相关联的表的名称 下面词典对象选择表,然后点击编辑,处 ...

  4. linux下的命令自动补齐增强

    linux 7 下 安装 bash-completion 可以实现命令的参数的自动补齐

  5. C++导言与入门

    写在开始 计算机编程语言: Remember that a program is just a sequence of instructions telling a computer what to ...

  6. Jmeter如何录制APP客户端脚本

    简单五步教大家Jmeter录制APP客户端脚本: Step1 右键单击该测试计划,选择"添加"-"线程组",添加一个线程组. Step2 为了录制客户端的操作, ...

  7. JAVA SSM整合流程以及注意点

    1.搭建整合环境 整合说明:SSM整合可以使用多种方式,咱们会选择XML + 注解的方式 先搭建整合的环境 先把Spring的配置搭建完成 再使用Spring整合SpringMVC框架 最后使用Spr ...

  8. 两个报文是如何进行 TCP 分组传输

    16 | 如何理解TCP的"流"? https://time.geekbang.org/column/article/132443 TCP 是一种流式协议在前面的章节中,我们讲的都 ...

  9. 洛谷 P6362 平面欧几里得最小生成树

    题目描述 平面上有 \(n\) 个点,第 \(i\) 个点坐标为 \((x_i, y_i)\).连接 \(i, j\) 两点的边权为 \(\sqrt{(x_i - x_j) ^ 2 + (y_i - ...

  10. Node 使用webpack编写简单的前端应用

    编写目的 1. 使用 Node 依赖webpack.jQuery编写简单的前端应用. 操作步骤 (1)新建一个目录 $ mkdir simple-app-demo $ cd simple-app-de ...