题目背景

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. 【Linux】记一次xfs分区数据恢复

    项目有一块磁盘无法挂载,而且还没有做RAID.... # mount /dev/sda /xxx 报错 mount: special device /dev/sda/ does not exist   ...

  2. Django - WebSocket:dwebsocket

    Django - WebSocket:dwebsocket 什么是WebSocket WebSocket是一种在单个TCP连接上进行全双工通信的协议 WebSocket使得客户端和服务器之间的数据交换 ...

  3. numpy模块(详解)

    重点 索引和切片 级联 聚合操作 统计操作 矩阵 什么是数据分析 是把隐藏在一些看似杂乱无章的数据背后的信息提炼出来,总结出所研究对象的内在规律 数据分析是用适当的方法对收集来的大量数据进行分析,帮助 ...

  4. (08)-Python3之--类和对象

    1.定义 类:类是抽象的,一类事物的共性的体现.  有共性的属性和行为. 对象:具体化,实例化.有具体的属性值,有具体做的行为. 一个类 对应N多个对象. 类包含属性以及方法. class 类名: 属 ...

  5. DNS是如何工作的?

    今天很多人都在讲域名系统和互联网作为一个整体是如何工作的,域名系统---也就是大家所熟知的DNS.不幸的是,对于天龙人和普通人来说,他们并不了解DNS到底是什么鬼.今天就来聊聊DNS,和那些想了解DN ...

  6. 一篇文章带你初步了解—CSS特指度

    CSS特指度 说明 这篇博客在在两台电脑上分别完成的,故而有些截图是Firefox,有些是Chrome,有些改动了浏览器的用户样式表,有些没改,但不会影响阅读,特此说明,勿怪. CSS选择器 单个CS ...

  7. postgresql 知识的整理

    .example { background-color: rgba(229, 236, 243, 1); color: rgba(0, 0, 0, 1); padding: 0.5em; margin ...

  8. 这几个小技巧,让你书写不一样的Vue!

    前言 最近一直在阅读Vue的源码,发现了几个实战中用得上的小技巧,下面跟大家分享一下. 同时也可以阅读我之前写的Vue文章 vue开发中的"骚操作" 挖掘隐藏在源码中的Vue技巧! ...

  9. (G)I-DLE—화(火花) (HWAA)

    闲来无事又来推歌了/cy 我这博客好像只能用来更日记+推歌了/kk 到今天(G)I-DLE已经获得九个一位啦~ 歌真的挺不错的 特别是,一个韩国女团出了这首歌的中文版 就觉得很有好感 music 韩文 ...

  10. Java——时间和日期处理

    Date Date date = new Date(); 获取时间 Date d = new Date(); // Date d2=new Date(System.currentTimeMillis( ...