【刷题】洛谷 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\) 分。

对于测试点 \(1\) ~ \(6\) ,时间限制为 \(1s\) 。
对于测试点 \(7\) ~ \(10\) ,时间限制为 \(2s\) 。
对于所有测试点,有 \(0 \le V_i \le 1000\) ,保证每个点可被采集的子串不超过 \(100000\) 个。
样例#1解释放在题面里了。
样例#2解释:
每个子串都不满足条件。
串a的排名是 \(4\) ,重要度和都是 \(1\) 。
串aa的排名是 \(3\) ,重要度和都是 \(2\) 。
串aaa的排名是 \(2\) ,重要度和都是 \(3\) 。
串aaaa的排名是 \(1\) ,重要度和都是 \(4\) 。
样例 #3解释:
串a的排名是 \(3\) ,重要度和都是 \(1\) 。
串aa的排名是 \(2\) ,重要度和都是 \(2\) ,共有两个串aa,位置分别为 \(1\) ~ \(2\) 和 \(2\) ~ \(3\) 。
串aaa的排名是 \(1\) ,重要度和都是 \(3\) 。
样例 #4解释:
可以发现,串 \(2\) ~ \(3\) (第二个aa)不满足条件了。它的排名还是 \(2\) 不变,但是重要度和为 \(3\) 。
题解
SA+线段树
转换一下考虑每个子串的方法
因为一个字符串的每一个后缀的前缀都是原串的一个子串,所以从后缀方面考虑
对于一个后缀,看它的前缀(即一个子串)。这个前缀越长,那么权值之和就越大,但是倒序排名就越小(写写看),即这两个值随前缀的长度,一个上升,一个下降。
这样的两条线只会有一个交点。又由于这其实并不是两条线,而是两堆点构成的虚线,所以还不一定有交点。
于是我们知道,对于一个后缀,至多只有一个它的前缀满足题目条件。题目数据范围中每个点可被采集的子串的最大值与字符串的长度相同也暗示了这个性质。
那么枚举后缀,二分两条虚线的交点(暂时当做有交点),那么我们需要快速得知一段长度的权值和与某个后缀的前缀在所有子串中的字典序倒序排名。
权值和直接前缀和就好了,很简单;关键在于找排名。
对字符串求SA与height,那么找一个后缀和前缀的rk,先找到在SA中这个前缀最早出现的位置。从当前后缀在SA中的位置开始寻找,一直找到height值小于我们的后缀的前缀的长度的位置,其对应的两个后缀中后面那个就是SA中这个前缀最早出现的位置。
于是可以用线段树维护height数组,方便这个rk位置的查询
找到位置后,类似求本质不同子串的方法。对于一个后缀,它的所有前缀代表的子串在所有子串的排名一定是连续的一段,并且也是按照SA的顺序来的(画一画就知道为什么了)。
那么就可以当做求本质不同子串,一个后缀 \(i\) 能够造成的新子串的贡献是 \(n-SA[i]+1-height[i]\),那么后面的子串的排名也要加这么多。所以累个前缀和就好了
同时,一个前缀在它所属的后缀中的排序就是它的长度
搞定了这些,这道题就做完了,复杂度 \(O(nlog^2n)\) ,虽然没有 \(O(nlogn)\) 的优秀,但还是可以过的
#include<bits/stdc++.h>
#define ui unsigned int
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
const int MAXN=200000+10;
char s[MAXN];
ll n,m,SA[MAXN],height[MAXN],rk[MAXN],nxt[MAXN],cnt[MAXN],val[MAXN],nt;
ll str[MAXN],sv[MAXN];
struct node{
ll l,r;
};
node ans[MAXN];
template<typename T> inline void read(T &x)
{
T data=0,w=1;
char ch=0;
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')w=-1,ch=getchar();
while(ch>='0'&&ch<='9')data=((T)data<<3)+((T)data<<1)+(ch^'0'),ch=getchar();
x=data*w;
}
template<typename T> inline void write(T x,char ch='\0')
{
if(x<0)putchar('-'),x=-x;
if(x>9)write(x/10);
putchar(x%10+'0');
if(ch!='\0')putchar(ch);
}
template<typename T> inline void chkmin(T &x,T y){x=(y<x?y:x);}
template<typename T> inline void chkmax(T &x,T y){x=(y>x?y:x);}
template<typename T> inline T min(T x,T y){return x<y?x:y;}
template<typename T> inline T max(T x,T y){return x>y?x:y;}
#define Mid ((l+r)>>1)
#define ls rt<<1
#define rs rt<<1|1
#define lson ls,l,Mid
#define rson rs,Mid+1,r
struct Segment_Tree{
ll Mn[MAXN<<2];
inline void PushUp(int rt)
{
Mn[rt]=min(Mn[ls],Mn[rs]);
}
inline void Build(int rt,int l,int r)
{
if(l==r)Mn[rt]=height[l];
else Build(lson),Build(rson),PushUp(rt);
}
inline ll Query(int rt,int l,int r,ll ps,ll k)
{
if(l==r)return l-(Mn[rt]>=k?1:0);
else
{
if(ps>=r)
{
if(Mn[rs]<k)return Query(rson,ps,k);
else return Query(lson,ps,k);
}
else
{
if(ps<=Mid||(ps>Mid&&Mn[rs]>=k))return Query(lson,ps,k);
else return Query(rson,ps,k);
}
}
}
};
Segment_Tree T;
#undef Mid
#undef ls
#undef rs
#undef lson
#undef rson
inline void GetSA()
{
m=300;
for(register int i=1;i<=n;++i)rk[i]=s[i];
for(register int i=1;i<=n;++i)cnt[rk[i]]++;
for(register int i=1;i<=m;++i)cnt[i]+=cnt[i-1];
for(register int i=n;i>=1;--i)SA[cnt[rk[i]]--]=i;
for(register int k=1,ps;k<=n;k<<=1)
{
ps=0;
for(register int i=n-k+1;i<=n;++i)nxt[++ps]=i;
for(register int i=1;i<=n;++i)
if(SA[i]>k)nxt[++ps]=SA[i]-k;
for(register int i=1;i<=m;++i)cnt[i]=0;
for(register int i=1;i<=n;++i)cnt[rk[i]]++;
for(register int i=1;i<=m;++i)cnt[i]+=cnt[i-1];
for(register int i=n;i>=1;--i)SA[cnt[rk[nxt[i]]]--]=nxt[i];
std::swap(rk,nxt);
rk[SA[1]]=1,ps=1;
for(register int i=2;i<=n;rk[SA[i]]=ps,++i)
if(nxt[SA[i]]!=nxt[SA[i-1]]||nxt[SA[i]+k]!=nxt[SA[i-1]+k])ps++;
if(ps>=n)break;
m=ps;
}
for(register int i=1,j,k=0;i<=n;height[rk[i++]]=k)
for(k=k?k-1:k,j=SA[rk[i]-1];s[i+k]==s[j+k];++k);
}
inline ll query(ll ps,ll len)
{
ps=T.Query(1,1,n,ps,len);
ps--;
return (ll)str[n]-str[ps]-len+1+height[ps+1];
}
int main()
{
scanf("%s",s+1);
n=strlen(s+1);
for(register int i=1;i<=n;++i)read(val[i]),sv[i]=sv[i-1]+val[i];
GetSA();
T.Build(1,1,n);
for(register int i=1;i<=n;++i)str[i]=str[i-1]+n-SA[i]+1-height[i];
for(register int i=1;i<=n;++i)
{
ll l=i,r=n,lt=log(r-l+1)/log(2),cl=0;
while(l<=r)
{
ll mid=(l+r)>>1,sum=sv[mid]-sv[i-1],nrk=query(rk[i],mid-i+1);
if(sum==nrk)
{
ans[++nt]=(node){i,mid};
break;
}
if(sum>nrk)r=mid-1;
else l=mid+1;
cl++;
if(cl>lt)break;
}
}
write(nt,'\n');
for(register int i=1;i<=nt;++i)write(ans[i].l,' '),write(ans[i].r,'\n');
return 0;
}
【刷题】洛谷 P4143 采集矿石的更多相关文章
- 洛谷 P4143 采集矿石 后缀数组
题目背景 ZRQ 成功从坍塌的洞穴中逃了出来.终于,他看到了要研究的矿石.他想挑一些带回去完成任务. 题目来源:Zhang_RQ哦对了 \(ZRQ\) 就他,嗯 题目描述 ZRQ 发现这里有 \(N\ ...
- [Luogu P4143] 采集矿石 [2018HN省队集训D5T3] 望乡台platform
[Luogu P4143] 采集矿石 [2018HN省队集训D5T3] 望乡台platform 题意 给定一个小写字母构成的字符串, 每个字符有一个非负权值. 输出所有满足权值和等于这个子串在所有本质 ...
- 2018.10.30 一题 洛谷4660/bzoj1168 [BalticOI 2008]手套——思路!问题转化与抽象!+单调栈
题目:https://www.luogu.org/problemnew/show/P4660 https://www.lydsy.com/JudgeOnline/problem.php?id=1168 ...
- 题解 洛谷 P4143 【采集矿石】
对于一个固定的左端点,右端点向右移动时,其子串权值和不断增大,字典序降序排名不断减小,因此对于一个左端点,最多存在一个右端点使其满足条件. 所以可以枚举左端点,然后二分右端点的位置,权值和通过前缀和来 ...
- AC日记——大爷的字符串题 洛谷 P3709
大爷的字符串题 思路: 莫队,需开O2,不开50: 代码: #include <bits/stdc++.h> using namespace std; #define maxn 20000 ...
- Mychael原创题 洛谷T23923 Mychaelの水题 【题解】
原题链接 题目大意: 有来自三个地区的人各a,b,c位,他们排成了一排.请问有多少种不同类型的排法,使得相邻的人都来自不同的地区 \(a,b,c<=200\) 答案取模 题解 弱弱的标程解法 设 ...
- 最短路径Dijkstra算法模板题---洛谷P3371 【模板】单源最短路径(弱化版)
题目背景 本题测试数据为随机数据,在考试中可能会出现构造数据让SPFA不通过,如有需要请移步 P4779. 题目描述 如题,给出一个有向图,请输出从某一点出发到所有点的最短路径长度. 输入格式 第一行 ...
- [网络流24题] 洛谷P2761 软件补丁问题
题意:某公司发现其研制的一个软件中有 n个错误,随即为该软件发放了一批共 m 个补丁程序.对于每一个补丁 i ,都有 2 个与之相应的错误集合 B1(i)和 B2(i),使得仅当软件包含 B1(i)中 ...
- 高精度加法——经典题 洛谷p1601
题目背景 无 题目描述 高精度加法,x相当于a+b problem,[b][color=red]不用考虑负数[/color][/b] 输入输出格式 输入格式: 分两行输入a,b<=10^500 ...
随机推荐
- python--自定义模块
python模块说明:类似于函数式编程和面向过程编程,函数式编程则完成一个功能,其他代码用来调用即可,提供了代码的重用性和代码间的耦合.而对于一个复杂的功能来,可能需要多个函数才能完成(函数又可以在不 ...
- 2018百度之星开发者大赛-paddlepaddle学习
前言 本次比赛赛题是进行人流密度的估计,因为之前看过很多人体姿态估计和目标检测的论文,隐约感觉到可以用到这次比赛上来,所以趁着现在时间比较多,赶紧报名参加了一下比赛,比赛规定用paddlepaddle ...
- 深圳第XX天
今天早晨,面了一家小公司.先说结果吧,面过了.但是,总感觉太假了.面试中很多问题都没有回答上来.然后老板看了一下简历,问:期薪资多少?我想了想,说7000.啊,要不留下来看看?我答应了.不到十分钟,就 ...
- 论文笔记:Visualizing and Understanding Convolutional Networks
2014 ECCV 纽约大学 Matthew D. Zeiler, Rob Fergus 简单介绍(What) 提出了一种可视化的技巧,能够看到CNN中间层的特征功能和分类操作. 通过对这些可视化信息 ...
- 算法笔记(c++)--01背包问题
算法笔记(c++)--经典01背包问题 算法解释起来太抽象了.也不是很好理解,最好的办法就是一步步写出来. 背包问题的核心在于m[i][j]=max(m[i-1][j],m[i-1][j-w[i]]+ ...
- DruidDataSource源码分析
最近公司要求基于阿里的DruidDataSource来做一个连接池监控 , 正好之前没有看过DruidDataSource的源码 , 便自己看了四个多小时写了一些自己的理解 , 给大家分享一下 , 如 ...
- 使用Scrapy构建一个网络爬虫
记得n年前项目需要一个灵活的爬虫工具,就组织了一个小团队用Java实现了一个爬虫框架,可以根据目标网站的结构.地址和需要的内容,做简单的配置开发,即可实现特定网站的爬虫功能.因为要考虑到各种特殊情形, ...
- python中 try、except、finally 的执行顺序
def test1(): try: print('to do stuff') raise Exception('hehe') print('to return in try') return ...
- 第一次c++团队合作作业期间第一篇随笔
分析了自己分到的任务,我的理解是这样的:首先要生成程序主框架,在主框架中进行地图的描绘.我应该是先进行地图的拼接,把建筑物和地面都拼接好.然后再在地图上显示出英雄和小兵.同时还要在主框架中分析了自己分 ...
- 第六章 过滤器Filter
Filter概述 Filter不用于客户端请求,只用于对request,response进行修改或对context,session,request事件进行监听. 1.概述 如上图,多个filter组成 ...