洛谷 P4143 采集矿石 后缀数组
题目背景
ZRQ 成功从坍塌的洞穴中逃了出来。终于,他看到了要研究的矿石。他想挑一些带回去完成任务。
题目来源:Zhang_RQ哦对了 \(ZRQ\) 就他,嗯
题目描述
ZRQ 发现这里有 \(N\) 块排成一排的矿石。
他用一个小写字母来表示每块矿石,他还发现每块矿石有一个重要度 \(V_i\)。
ZRQ 想采集一段连续的矿石回研究所。
他非常严格,被采集的一段矿石必须满足小写字母的字典序降序排名等于这段矿石的重要度和。
这里多个出现在不同位置的本质相同串的字典序排名相同。
比如说字母串为 aa,那么第一个 a 的排名和第二个 a 的排名相同,都是 2(第 1 是 aa)。
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 采集矿石 后缀数组的更多相关文章
- 【刷题】洛谷 P4143 采集矿石
题目背景 ZRQ成功从坍塌的洞穴中逃了出来.终于,他看到了要研究的矿石.他想挑一些带回去完成任务. 题目来源:Zhang_RQ哦对了ZRQ就他,嗯 题目描述 ZRQ发现这里有 \(N\) 块排成一排的 ...
- 洛谷P3763 [TJOI2017]DNA(后缀数组 RMQ)
题意 题目链接 Sol 这题打死我也不会想到后缀数组的,应该会全程想AC自动机之类的吧 但知道这题能用后缀数组做之后应该就不是那么难了 首先把\(S\)和\(S0\)拼到一起跑,求出Height数组 ...
- 洛谷-P3809-后缀排序(后缀数组)
看了求后缀数组的倍增法之后很快就理解了,但是自己写的倍增法用map排序还是超时了.然后看了两天别人写的模板,题目是通过了,但感觉代码还是半懂半背的.以后多熟悉熟悉吧: 后缀数组 #include &q ...
- 洛谷3809 SA模板 后缀数组学习笔记(复习)
其实SA这个东西很久之前就听过qwq 但是基本已经忘的差不多了 嘤嘤嘤 QWQ感觉自己不是很理解啊 所以写不出来那种博客 QWQ只能安利一些别人的博客了 小老板 真的是讲的非常好 不要在意名字 orz ...
- 洛谷P5108 仰望半月的夜空(后缀数组)
题意 题目链接 Sol warning:下面这个做法只有95分,本地拍了1w+组都没找到错误我表示十分无能为力 我们考虑每个串的排名去更新答案,显然排名为\(1\)的后缀的前缀一定是当前长度的字典序最 ...
- [Luogu P4143] 采集矿石 [2018HN省队集训D5T3] 望乡台platform
[Luogu P4143] 采集矿石 [2018HN省队集训D5T3] 望乡台platform 题意 给定一个小写字母构成的字符串, 每个字符有一个非负权值. 输出所有满足权值和等于这个子串在所有本质 ...
- 题解 洛谷 P4143 【采集矿石】
对于一个固定的左端点,右端点向右移动时,其子串权值和不断增大,字典序降序排名不断减小,因此对于一个左端点,最多存在一个右端点使其满足条件. 所以可以枚举左端点,然后二分右端点的位置,权值和通过前缀和来 ...
- 洛谷 P3688 - [ZJOI2017]树状数组(二维线段树+标记永久化)
题面传送门 首先学过树状数组的应该都知道,将树状数组方向写反等价于前缀和 \(\to\) 后缀和,因此题目中伪代码的区间求和实质上是 \(sum[l-1...n]-sum[r...n]=sum[l-1 ...
- 【洛谷P3919】可持久化数组
题目大意:需要维护一个长度为 N 的数组,支持在历史版本上单点修改和单点查询. 题解:显然,如果直接暴力维护的话会 MLE.因此,采用线段树进行维护,使得空间复杂度由 \(O(mn)\) 降至 \(O ...
随机推荐
- 主题模型值LDA
主题模型(topic model)是以非监督学习的方式对文集的隐含语义结构(latent semantic structure)进行聚类(clustering)的统计模型. 主题模型主要被用于自然语言 ...
- C++ STL 栈和队列
栈和队列 头文件 #include<queue> // 队列 #include<stack> //栈 定义方式 //参数就是数据类型 stack<int> s; q ...
- Java中的Date类型无法赋值给数据库的datetime类型
因为Java中new Date()的结果是"Thu Aug 27 19:03:54 CST 2020",而mysql中的datetime不接受这样的日期格式,插入数据会报错. 解决 ...
- LocalDateTime、OffsetDateTime、ZonedDateTime互转,这一篇绝对喂饱你
前言 你好,我是A哥(YourBatman). 在JSR 310日期时间体系了,一共有三个API可用于表示日期时间: LocalDateTime:本地日期时间 OffsetDateTime:带偏移量的 ...
- python工业互联网应用实战3—Django Admin列表
Django Admin笔者使用下来可以说是Django框架的开发利器,业务model构建完成后,我们就能快速的构建一个增删查改的后台管理框架.对于大量的企业管理业务开发来说,可以快速的构建一个可发布 ...
- selenium元素等待的三种方法
1.强制等待sleep() 使用方法:sleep(X),等待X秒后,进行下一步操作. 使用最简单的一种办法就是强制等待sleep(X),强制让浏览器等待X秒,不管当前操作是否完成,是否可以进行下一步操 ...
- 超详细oracle 11g安装步骤 win版本
1. 打开网址: https://edelivery.oracle.com 使用oracle 任意账号登录 账号:2696671285@qq.com 密码:Oracle123 感谢来自某位好心大佬的共 ...
- NULL-safe equal null 索引 空字符串
小结 1. mysql> INSERT INTO my_table (phone) VALUES (NULL); 有手机号但是不知道 mysql> INSERT INTO my_table ...
- SpringMVC听课笔记(十一:国际化)
1. 关于国际化 -- 在页面上根据浏览器的语言设置情况对文本(不是内容),时间,数值进行本地化处理 使用JSTL的fmt标签 -- 可以在bean中获取国际化资源文件 Locale对应的消息 在be ...
- GeoServer发布Shapfile、PostGIS数据
GeoServer发布Shapfile.PostGIS数据 一.GeoServer发布Shapfile数据 1.1 创建工作区 1.1.1 工作区 1.2 在工作区中加入新的数据存储 1.2.1 数据 ...