[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的更多相关文章

  1. [2018HN省队集训D9T1] circle

    [2018HN省队集训D9T1] circle 题意 给定一个 \(n\) 个点的竞赛图并在其中钦定了 \(k\) 个点, 数据保证删去钦定的 \(k\) 个点后这个图没有环. 问在不删去钦定的这 \ ...

  2. [2018HN省队集训D8T1] 杀毒软件

    [2018HN省队集训D8T1] 杀毒软件 题意 给定一个 \(m\) 个01串的字典以及一个长度为 \(n\) 的 01? 序列. 对这个序列进行 \(q\) 次操作, 修改某个位置的字符情况以及查 ...

  3. [2018HN省队集训D8T3] 水果拼盘

    [2018HN省队集训D8T3] 水果拼盘 题意 给定 \(n\) 个集合, 每个集合包含 \([1,m]\) 中的一些整数, 在这些集合中随机选取 \(k\) 个集合, 求这 \(k\) 个集合的并 ...

  4. [2018HN省队集训D6T2] girls

    [2018HN省队集训D6T2] girls 题意 给定一张 \(n\) 个点 \(m\) 条边的无向图, 求选三个不同结点并使它们两两不邻接的所有方案的权值和 \(\bmod 2^{64}\) 的值 ...

  5. [2018HN省队集训D5T2] party

    [2018HN省队集训D5T2] party 题意 给定一棵 \(n\) 个点以 \(1\) 为根的有根树, 每个点有一个 \([1,m]\) 的权值. 有 \(q\) 个查询, 每次给定一个大小为 ...

  6. [2018HN省队集训D5T1] 沼泽地marshland

    [2018HN省队集训D5T1] 沼泽地marshland 题意 给定一张 \(n\times n\) 的棋盘, 对于位置 \((x,y)\), 若 \(x+y\) 为奇数则可能有一个正权值. 你可以 ...

  7. [Codeforces 321D][2018HN省队集训D4T2] Ciel and Flipboard

    [Codeforces 321D][2018HN省队集训D4T2] Ciel and Flipboard 题意 给定一个 \(n\times n\) 的矩阵 \(A\), (\(n\) 为奇数) , ...

  8. [2018HN省队集训D1T3] Or

    [2018HN省队集训D1T3] Or 题意 给定 \(n\) 和 \(k\), 求长度为 \(n\) 的满足下列条件的数列的数量模 \(998244353\) 的值: 所有值在 \([1,2^k)\ ...

  9. [2018HN省队集训D1T1] Tree

    [2018HN省队集训D1T1] Tree 题意 给定一棵带点权树, 要求支持下面三种操作: 1 root 将 root 设为根. 2 u v d 将以 \(\operatorname{LCA} (u ...

随机推荐

  1. js 3行代码,最简易实现div效果悬浮

    简易实现浮动效果的首要因素是:获取滚动条距离浏览器顶部的距离,下面直接贴代码: <!DOCTYPE html> <html> <head> <meta cha ...

  2. ZOJ 1586 QS Network(Kruskal算法求解MST)

    题目: In the planet w-503 of galaxy cgb, there is a kind of intelligent creature named QS. QScommunica ...

  3. Linux 文件IO管理 - POSIX

    以下是对POSIX的简短解释: POSIX表示可移植操作系统接口(Portable Operating System Interface of UNIX,缩写为 POSIX ),POSIX标准定义了操 ...

  4. Python魔法方法之属性访问 ( __getattr__, __getattribute__, __setattr__, __delattr__ )

    通常情况下,我们在访问类或者实例对象的时候,会牵扯到一些属性访问的魔法方法,主要包括: ① __getattr__(self, name): 访问不存在的属性时调用 ② __getattribute_ ...

  5. ionic中generate page后module.ts报错的解决办法

    此问题出现在Ionic官方将版本从2.2升级到Ionic3以上之后, 在项目中generate page时,自动创建的module.ts就报错,如下: 解决办法如下: 1)将IonicModule替换 ...

  6. C#字符串中的中文逗号转英文逗号

    public static string ToDBC(string input) { char[] c = input.ToCharArray(); for (int i = 0; i < c. ...

  7. C#观察者模式的应用与思考

    1:项目场景 在设计数据表的时候有时候为了将来统计或查询的方便,我们会冗余一些字段.如有三张数据表,学校信息表.班级动态表.班级信息表. 班级动态由学校老师所发,可以进行评论点赞等操作,为了提升这种非 ...

  8. LOJ#6271. 「长乐集训 2017 Day10」生成树求和 加强版

    传送门 由于是边权三进制不进位的相加,那么可以考虑每一位的贡献 对于每一位,生成树的边权相当于是做模 \(3\) 意义下的加法 考虑最后每一种边权的生成树个数,这个可以直接用生成函数,在矩阵树求解的时 ...

  9. cf1097D. Makoto and a Blackboard(期望dp)

    题意 题目链接 Sol 首先考虑当\(n = p^x\),其中\(p\)是质数,显然它的因子只有\(1, p, p^2, \dots p^x\)(最多logn个) 那么可以直接dp, 设\(f[i][ ...

  10. htm-文字标签和注释标签

    文字标签:修改文字的样式 <font></font>  属性: size:文字的大小 取值范围 1-7,超出了7,默认还是7 color:文字颜色 两种表示方法 英文单词:re ...