题目背景

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. kubernets之namespace

    一 命名空间的介绍以及作用 1  概念 为了方便不同部门之间对kubernets集群的使用,并且对其进行有效的隔离,kubernets提供了一种资源隔离手段,通过将各种不同资源分组到 一个区域,并且统 ...

  2. LeetCode617. 合并二叉树

    题目 1 class Solution { 2 public: 3 TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) { 4 if(!t1 && ...

  3. Kafka 探险 - 生产者源码分析: 核心组件

    这个 Kafka 的专题,我会从系统整体架构,设计到代码落地.和大家一起杠源码,学技巧,涨知识.希望大家持续关注一起见证成长! 我相信:技术的道路,十年如一日!十年磨一剑! 往期文章 Kafka 探险 ...

  4. StringBuilder和输入输出

    构建字符串(StringBuilder的应用) 有些时候,需要由较短的字符串构建字符串,例如:按键或来自文件的单词,采用字符串连接的方式达到此目的效率比较低.每次连接字符串,都会构建一个新的Strin ...

  5. 我在华为OD的275天

    目录 0 - 时间线 1 - 为什么会去华为 OD 2 - 华为 OD 的工作内容 3 - OD 与华为自有员工的对比 4 - 那,到底要不要去华为 OD? 5 - 网传的 OD 转华为正编,真的假的 ...

  6. redis修改requirepass 参数 改密码

    1. 不重启redis如何配置密码? a. 在配置文件中配置requirepass的密码(当redis重启时密码依然有效). # requirepass foobared  ->  修改成 :  ...

  7. 全栈性能测试修炼宝典-JMeter实战笔记(二)

    性能测试初体验 性能测试实质:利用工具去模拟大量用户操作来验证系统能够承受的负载情况,找出潜在的性能问题,分析并解决:找出系统性能变化趋势,为后续的扩展提供参考 测试分类 测试内容中,负载测试.压力测 ...

  8. jmeter的线程数,并发用户数,TPS,RPS 关系解说

    背景 在做性能测试的时候,传统方式都是用并发虚拟用户数来衡量系统的性能(站在客户端视角),一般适用于一些网页站点例如首页.H5的压测:而RPS(Requests per second)模式主要是为了方 ...

  9. Edition-Based Redefinition

    Oracle在11g引入了Edition-Based Redefinition(EBR),主要是为了解决在更新数据库对象,比如PL/SQL程序,视图等,如果该对象被锁住了,会导致更新必须等待,如果要使 ...

  10. moco框架加入cookies

    一.带cookie信息的get请求 注意:cookie是放在request里的,一般登录的场景这些会用到 1.代码 2.接口管理工具添加 注意:cooike的域和路径都要添加 二.带cookie信息的 ...