[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 ...
随机推荐
- golang基础--常量与运算符
常量与运算符 常量的定义 常量的值在编译时已经确定 常量的定义格式与变量的基本相同 等号的右侧必须是常量或者常量表达式 规范: 常量定义时建议都使用大写,单词之间使用大写 便于包的外部调用 如果只限于 ...
- Beta阶段——Scrum 冲刺博客第二天
一.当天站立式会议照片一张 二.每个人的工作 (有work item 的ID),并将其记录在码云项目管理中 昨天已完成的工作 实现对index界面的重新制作,变成了原来的main界面,直接在该界面输入 ...
- Window环境配置Mongodb
Mongodb这几天也了解了一下,今天配置了下环境,从今天开始学下Mongodb数据库. 一.下载 在这个网址中选择要下载的开发环境https://www.mongodb.com/download-c ...
- [转]查看SQL Server被锁的表以及如何解锁
本文转自:https://www.cnblogs.com/shy1766IT/p/6225694.html 锁定数据库的一个表的区别 SELECT * FROM table WITH (HOLDLOC ...
- Ionic3与Angular4新特性
之前(17年3月底)Angular4.0.0正式发布,这个月(4月十几号)Ionic3又发布了,很多人看到这个估计都是一脸懵圈,其实,Angular4只是Angular2的后续版本,Ionic3也是I ...
- The type org.springframework.jms.JmsException cannot be resolved报错解决
在调用JmsTemplate的send方法时,一直报编译时异常.如下: 异常提示是无法解析org.SpringFrawork.jms.JmsException类型.如下: The type org.s ...
- Jquery链式编程及Index()详解
Jquery中的方法基本 上都可以返回一个Jquery对象, 如: <body> <div class="divcontent"> <p>中国& ...
- Linux排查Java程序占用CPU很高的解决办法
Java的工具集相当强大,学习成本也很低,处理线上问题时,jstack这个工具就比微软的windbg,好学好用很多,3步找出占用CPU很高的源所在.而windbg反人类的各种命令,实在不敢恭维. 故意 ...
- 163. [USACO Mat07] 牛语
☆ 输入文件:latin.in 输出文件:latin.out 简单对比 时间限制:1 s 内存限制:128 MB 译: zqzas 奶牛们听说猪发明了一种秘密语言,叫做"猪语 ...
- vue-router 实现导航守卫(路由卫士)
路由跳转前做一些验证,比如登录验证,是网站中的普遍需求. 对此,vue-route 提供的 beforeRouteUpdate 可以方便地实现导航守卫(navigation-guards). 导航守卫 ...