[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 ...
随机推荐
- SpringMVC 之 Hello World 入门
1 准备开发环境和运行环境 依赖 jar 包下载,如下图所示: 2 前端控制器的配置 在我们的web.xml中添加如下配置: <!-- The front controller of this ...
- Redis--redis集群环境搭建
1.redis-cluster架构图 Redis 自3.0以后开始支持集群.从上图我们可以看出,redis集群的每个节点之间都进行相互通信,在redis集群中,不存在代理层,即没有固定的入口.redi ...
- BG.Hive - part3
1. Hive数据导入 - Load Load,加载,不会进行任何数据转换和处理的操作,只会进行数据Move操作,将元数据移动到HDFS指定目录 a> hdfs dfs -put 'hdfs的h ...
- 照猫画虎owin oauth for qq and sina
ms随vs2013推出了mvc5,mvc5自带的模板项目中引用了新的身份认证框架 ms identity.其中owin部分实现了google,facebook,twitter等国外常见的第三方用户.可 ...
- ABP学习入门系列(三) (领域层中的仓储Repository)
一,仓储定义:“在领域层和数据映射层的中介,使用类似集合的接口来存取领域对象”(Martin Fowler) . 仓储用来操作数据库进行数据存取.仓储接口在领域层定义,而仓储的实现类应该写在基础设施层 ...
- Nginx代理后服务端使用remote_addr获取真实IP
直奔主题,在代理服务器的Nginx配置(yourWebsite.conf)的location /中添加: #获取客户端IP proxy_set_header Host $host; proxy_set ...
- win10 安装 oracle 11g 时遇到 [INS-13001] 环境不满足最低要求 的问题
前言:自己系统上安装 oracle 时报错,故记录下来. 环境: win10 x64 oracle 11g 安装包 出错: 解决方案:同 https://www.cnblogs.com/yuxiaol ...
- CodeForces760B
B. Frodo and pillows time limit per test:1 second memory limit per test:256 megabytes input:standard ...
- Java Web应用开发工具
java Web应用开发工具详细地址:https://my.oschina.net/gitosc/blog/1538466
- opencv3.2.0实现连续图片合成avi视频
##名称:利用videowriter实现多张连续图片合成avi视频 ##平台:QT5.7.1+OpenCV3.2.0 ##日期:2017年12月10日 /**************新建QT控制台程序 ...