[Luogu P4143] 采集矿石 [2018HN省队集训D5T3] 望乡台platform
[Luogu P4143] 采集矿石 [2018HN省队集训D5T3] 望乡台platform
题意
给定一个小写字母构成的字符串, 每个字符有一个非负权值. 输出所有满足权值和等于这个子串在所有本质不同子串按字典序降序排序后的排名的子串的数量及左右端点.
\(n\le 2\times 10^5\), 保证合法子串个数不超过 \(2\times 10^5\).
题解
我们看这个排名是按字典序逆序排的必有高论. 显然固定左端点后串长越长字典序越大排名越靠前, 而同时子串权值和会增大, 于是对于每个左端点其实最多只有一个满足条件的右端点...(所以那句保证其实是废话)
显然我们对于每个左端点二分这个可能的交点判一下就好了. 子串求和地球人都知道要用前缀和, 问题转化为如何快速求某个子串的排名.
这个后缀自动机好像不是很好搞QAQ...估计要对反串拉出一棵后缀树一看就很难写分明是你懒吧
考虑后缀数组. 我们发现每个后缀对本质不同的子串数量的贡献就是 \(height_{rank_i}\) 与后缀 \(i\) 的长度的差. 如果按后缀排名计算的话, 先算入的子串的字典序显然是要小的. 我们只要二分找到当前子串第一次出现时的左端点然后计算它之前出现的本质不同子串个数就好了.
字典序逆序同理, 前缀和变后缀和.
然后这题就完了. 复杂度两个 \(\log\) (比题解里的一个 \(\log\) 线段树做法快到不知哪里去了). 好像可以算后缀数组求子串排名板子题
参考代码
#include <bits/stdc++.h>
const int MAXN=2e5+10;
typedef long long intEx;
int n;
int a[MAXN];
char s[MAXN];
int SA[MAXN];
int lg[MAXN];
int cnt[MAXN];
int rank[MAXN];
intEx sum[MAXN];
int height[MAXN];
int st[20][MAXN];
int* x=new int[MAXN];
int* y=new int[MAXN];
void BuildSA();
void STBuild();
int STMin(int,int);
intEx Rank(int,int);
int main(){
scanf("%s",s+1);
n=strlen(s+1);
for(int i=1;i<=n;i++){
scanf("%d",a+i);
a[i]+=a[i-1];
}
BuildSA();
STBuild();
std::vector<std::pair<int,int>> ans;
for(int i=1;i<=n;i++){
int l=i,r=n+1;
while(r-l>1){
int mid=(l+r)>>1;
if(Rank(i,mid)>=a[mid]-a[i-1])
l=mid;
else
r=mid;
}
if(Rank(i,l)==a[l]-a[i-1])
ans.emplace_back(i,l);
}
printf("%d\n",int(ans.size()));
for(auto p:ans)
printf("%d %d\n",p.first,p.second);
return 0;
}
intEx Rank(int l,int r){
int L=0,R=rank[l];
while(R-L>1){
int mid=(L+R+1)>>1;
if(mid==rank[l]||STMin(mid+1,rank[l])>=(r-l+1))
R=mid;
else
L=mid;
}
// printf("$ [%d,%d]: first=%d\n",l,r,R);
return sum[R+1]+(n-SA[R]+1)-(r-l+1)+1;
}
void BuildSA(){
int m=127;
for(int i=1;i<=n;i++)
++cnt[x[i]=s[i]];
for(int i=1;i<=m;i++)
cnt[i]+=cnt[i-1];
for(int i=n;i>=1;i--)
SA[cnt[x[i]]--]=i;
for(int k=1;k<=n;k<<=1){
int p=0;
for(int i=n-k+1;i<=n;i++)
y[++p]=i;
for(int i=1;i<=n;i++)
if(SA[i]>k)
y[++p]=SA[i]-k;
memset(cnt+1,0,sizeof(int)*m);
for(int i=1;i<=n;i++)
++cnt[x[i]];
for(int i=1;i<=m;i++)
cnt[i]+=cnt[i-1];
for(int i=n;i>=1;i--)
SA[cnt[x[y[i]]]--]=y[i];
std::swap(x,y);
p=1;
x[SA[1]]=1;
for(int i=2;i<=n;i++)
x[SA[i]]=(y[SA[i]]==y[SA[i-1]]&&y[SA[i]+k]==y[SA[i-1]+k])?p:++p;
if(p>=n)
break;
m=p;
}
int k=0;
for(int i=1;i<=n;i++)
rank[SA[i]]=i;
for(int i=1;i<=n;i++){
if(rank[i]==1)
continue;
if(k)
--k;
int j=SA[rank[i]-1];
while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k])
++k;
height[rank[i]]=k;
}
for(int i=n;i>=1;i--)
sum[i]=n-SA[i]+1-height[i]+sum[i+1];
// for(int i=1;i<=n;i++){
// printf("SA[%d]=%d sum[%d]=%lld\n",i,SA[i],i,sum[i]);
// }
}
int STMin(int l,int r){
int len=(r-l+1);
return std::min(st[lg[len]][l],st[lg[len]][r-(1<<lg[len])+1]);
}
void STBuild(){
for(int i=2;i<=n;i<<=1)
lg[i]=1;
for(int i=2;i<=n;i++){
lg[i]+=lg[i-1];
st[0][i]=height[i];
}
for(int j=1;(1<<j)<=n;j++){
for(int i=2;i<=n;i++){
st[j][i]=st[j-1][i];
if(i+(1<<(j-1))<=n)
st[j][i]=std::min(st[j][i],st[j-1][i+(1<<(j-1))]);
}
}
}
[Luogu P4143] 采集矿石 [2018HN省队集训D5T3] 望乡台platform的更多相关文章
- [2018HN省队集训D9T1] circle
[2018HN省队集训D9T1] circle 题意 给定一个 \(n\) 个点的竞赛图并在其中钦定了 \(k\) 个点, 数据保证删去钦定的 \(k\) 个点后这个图没有环. 问在不删去钦定的这 \ ...
- [2018HN省队集训D8T1] 杀毒软件
[2018HN省队集训D8T1] 杀毒软件 题意 给定一个 \(m\) 个01串的字典以及一个长度为 \(n\) 的 01? 序列. 对这个序列进行 \(q\) 次操作, 修改某个位置的字符情况以及查 ...
- [2018HN省队集训D8T3] 水果拼盘
[2018HN省队集训D8T3] 水果拼盘 题意 给定 \(n\) 个集合, 每个集合包含 \([1,m]\) 中的一些整数, 在这些集合中随机选取 \(k\) 个集合, 求这 \(k\) 个集合的并 ...
- [2018HN省队集训D6T2] girls
[2018HN省队集训D6T2] girls 题意 给定一张 \(n\) 个点 \(m\) 条边的无向图, 求选三个不同结点并使它们两两不邻接的所有方案的权值和 \(\bmod 2^{64}\) 的值 ...
- [2018HN省队集训D5T2] party
[2018HN省队集训D5T2] party 题意 给定一棵 \(n\) 个点以 \(1\) 为根的有根树, 每个点有一个 \([1,m]\) 的权值. 有 \(q\) 个查询, 每次给定一个大小为 ...
- [2018HN省队集训D5T1] 沼泽地marshland
[2018HN省队集训D5T1] 沼泽地marshland 题意 给定一张 \(n\times n\) 的棋盘, 对于位置 \((x,y)\), 若 \(x+y\) 为奇数则可能有一个正权值. 你可以 ...
- [Codeforces 321D][2018HN省队集训D4T2] Ciel and Flipboard
[Codeforces 321D][2018HN省队集训D4T2] Ciel and Flipboard 题意 给定一个 \(n\times n\) 的矩阵 \(A\), (\(n\) 为奇数) , ...
- [2018HN省队集训D1T3] Or
[2018HN省队集训D1T3] Or 题意 给定 \(n\) 和 \(k\), 求长度为 \(n\) 的满足下列条件的数列的数量模 \(998244353\) 的值: 所有值在 \([1,2^k)\ ...
- [2018HN省队集训D1T1] Tree
[2018HN省队集训D1T1] Tree 题意 给定一棵带点权树, 要求支持下面三种操作: 1 root 将 root 设为根. 2 u v d 将以 \(\operatorname{LCA} (u ...
随机推荐
- Sqlserver 还原
--完整还原 RESTORE DATABASE demoData FROM DISK=N'D:\Backup\demoData.bak'; RESTORE DATABASE { database_na ...
- [PHP] 重回基础(IO流)
IO流用来处理设备之间的数据传输 java对数据的操作是通过流的方式 java用于操作流的对象都在IO包中 流按操作数据分为两种:字节流与字符流 流按流向分为:输入流,输出流 字符流 抽象基类:Rea ...
- 前端开发实用工具-Bower的使用。
参看博客:[https://segmentfault.com/a/1190000002971135]
- win10 安装oracle 11gR2_database出现universal Installer后闪退就没反应的解决方案
前言:本机为 win 10 64位系统,安装oracle win64_11gR2_database的时候出现问题,安装不了,经排查解决了此问题.转载请注明出处:https://www.cnblogs. ...
- 不使用JavaScript让IE浏览器支持HTML5元素——张鑫旭
by zhangxinxu from http://www.zhangxinxu.com本文地址:http://www.zhangxinxu.com/wordpress/?p=2515 如果我们不做什 ...
- CSS关于文本渲染的属性text-rendering
CSS关于文本渲染的属性text-rendering告诉渲染引擎工作时如何优化显示文本. 浏览器会在渲染速度.易读性(清晰度)和几何精度方面做一个权衡. 我们知道,SVG-可缩放矢量图形(Scalab ...
- 记一次简单爬虫(豆瓣/dytt)
磕磕绊绊学python一个月,这次到正则表达式终于能写点有趣的东西,在此作个记录: ————————————————————————————————————————————————— 1.爬取豆瓣电影 ...
- request获取当前用户
1.request.getRemoteUser();//获取当前缓存的用户,比如Spring Security做权限控制后就会将用户登录名缓存到这里 request.getRemoteAddr();/ ...
- LNMP下安装memcache
转自:LNMP 添加 memcached服务 由于memcached具有更多的功能和服务,已经不推荐使用memcache了.(缺少个字母d) 1. 首先安装memcached服务端. 这里使用yum源 ...
- YOLO object detection with OpenCV
Click here to download the source code to this post. In this tutorial, you’ll learn how to use the Y ...