[CF653F] Paper task

Description

给定一个括号序列,统计合法的本质不同子串的个数。

Solution

很容易想到,只要在传统统计本质不同子串的基础上修改一下即可。

考虑经典统计过程,对于第 \(i\) 个后缀,它的贡献为 \(n - sa[i] + 1 - h[i]\)

也就意味着,它产生贡献的区间是 \([sa[i]+h[i], n]\) 。换言之,对任意 \(j \in [sa[i]+h[i], n]\) , \(s[sa[i],j]\) 是一个答案。

那么我们现在就是要判断这些答案中有多少合法。也就是对某个 \(i\) ,有多少个 \(j \in [sa[i]+h[i], n]\) , ,满足 \(\sum_{k=sa[i]}^j a_k = sum[j]-sum[sa[i]-1]\) 为 \(0\)。 这里 \(a_i\) 表示括号序列,左括号对应 \(1\) ,右括号对应 \(-1\) 。

也就是询问下标在 \(sa[i]+h[i]\) 及之后, \(sum[k]=sum[sa[i]-1]\) 的 \(k\) 有多少个。

按数值插进若干个 std::vector 然后暴力二分即可。

但这样会忽略那些中途出现右括号比左括号多的情况。因此,对于每个后缀,我们在统计时要找到最远能到达的位置,即第一个小于等于 \(sum[sa[i]-1]-1\) 出现的位置,我们要把这个位置之后的结果减去。暴力扫一遍,权值线段树维护即可。

#include <bits/stdc++.h>
using namespace std; int n,m=256,sa[1000005],y[1000005],u[1000005],v[1000005],o[1000005],r[1000005],h[1000005],T;
int a[1000005],sum[1000005],lim[1000005];
char str[1000005];
long long ans;
vector <int> vec[1000005]; namespace seg {
int val[4000005];
void build(int p,int l,int r) {
if(l==r) {
val[p]=INT_MAX;
}
else {
build(p*2,l,(l+r)/2);
build(p*2+1,(l+r)/2+1,r);
val[p]=min(val[p*2],val[p*2+1]);
}
}
void modify(int p,int l,int r,int pos,int key) {
if(l==r) {
val[p]=key;
}
else {
if(pos<=(l+r)/2) modify(p*2,l,(l+r)/2,pos,key);
else modify(p*2+1,(l+r)/2+1,r,pos,key);
val[p]=min(val[p*2],val[p*2+1]);
}
}
int query(int p,int l,int r,int ql,int qr) {
if(l>qr || r<ql) return INT_MAX;
if(l>=ql && r<=qr) return val[p];
return min(query(p*2,l,(l+r)/2,ql,qr),query(p*2+1,(l+r)/2+1,r,ql,qr));
}
void modify(int pos,int key) {
modify(1,1,2*n+1,pos+n+1,key);
}
int query(int ql,int qr) {
return query(1,1,2*n+1,ql+n+1,qr+n+1);
}
void build() {
build(1,1,2*n+1);
}
} int calc(int val,int pos) {
return vec[val+500000].end() - lower_bound(vec[val+500000].begin(),vec[val+500000].end(),pos);
} int main(){
cin>>n;
cin>>str+1; seg::build(); for(int i=1;i<=n;i++) a[i]=str[i]=='('?1:-1;
for(int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i]; for(int i=1;i<=n;i++) vec[sum[i]+500000].push_back(i); for(int i=n;i>=1;--i) {
seg::modify(sum[i],i);
lim[i]=min(n,seg::query(-n,sum[i-1]-1));
} for(int i=1;i<=n;i++) u[str[i]]++;
for(int i=1;i<=m;i++) u[i]+=u[i-1];
for(int i=n;i>=1;i--) sa[u[str[i]]--]=i;
r[sa[1]]=1;
for(int i=2;i<=n;i++) r[sa[i]]=r[sa[i-1]]+(str[sa[i]]!=str[sa[i-1]]); for(int l=1;r[sa[n]]<n;l<<=1) {
memset(u,0,sizeof u);
memset(v,0,sizeof v);
memcpy(o,r,sizeof r);
for(int i=1;i<=n;i++) u[r[i]]++, v[r[i+l]]++;
for(int i=1;i<=n;i++) u[i]+=u[i-1], v[i]+=v[i-1];
for(int i=n;i>=1;i--) y[v[r[i+l]]--]=i;
for(int i=n;i>=1;i--) sa[u[r[y[i]]]--]=y[i];
r[sa[1]]=1;
for(int i=2;i<=n;i++) r[sa[i]]=r[sa[i-1]]+((o[sa[i]]!=o[sa[i-1]])||(o[sa[i]+l]!=o[sa[i-1]+l]));
}
{
int i,j,k=0;
for(int i=1;i<=n;h[r[i++]]=k)
for(k?k--:0,j=sa[r[i]-1];str[i+k]==str[j+k];k++);
} for(int i=1;i<=n;i++) ans+=calc(sum[sa[i]-1],sa[i]+h[i])-calc(sum[sa[i]-1],max(sa[i]+h[i],lim[sa[i]]+1));
cout<<ans<<endl;
}

[CF653F] Paper task - 后缀数组,线段树,vector的更多相关文章

  1. BZOJ 1396: 识别子串( 后缀数组 + 线段树 )

    这道题各位大神好像都是用后缀自动机做的?.....蒟蒻就秀秀智商写一写后缀数组解法..... 求出Height数组后, 我们枚举每一位当做子串的开头. 如上图(x, y是height值), Heigh ...

  2. 【XSY1551】往事 广义后缀数组 线段树合并

    题目大意 给你一颗trie树,令\(s_i\)为点\(i\)到根的路径上的字符组成的字符串.求\(max_{u\neq v}(LCP(s_u,s_v)+LCS(s_u,s_v))\) \(LCP=\) ...

  3. Luogu4770 NOI2018你的名字(后缀数组+线段树)

    即求b串有多少个本质不同的非空子串,在a串的给定区间内未出现.即使已经8102年并且马上就9102年了,还是要高举SA伟大旗帜不动摇. 考虑离线,将所有询问串及一开始给的串加分隔符连起来,求出SA.对 ...

  4. BZOJ 2865 字符串识别 | 后缀数组 线段树

    集训讲字符串的时候我唯一想出正解的题-- 链接 BZOJ 2865 题面 给出一个长度为n (n <= 5e5) 的字符串,对于每一位,求包含该位的.最短的.在原串中只出现过一次的子串. 题解 ...

  5. bzoj 1396: 识别子串 && bzoj 2865: 字符串识别【后缀数组+线段树】

    根据height数组的定义,和当前后缀串i最长的相同串的长度就是max(height[i],height[i+1]),这个后缀贡献的最短不同串长度就是len=max(height[i],height[ ...

  6. Codeforces 1063F - String Journey(后缀数组+线段树+dp)

    Codeforces 题面传送门 & 洛谷题面传送门 神仙题,做了我整整 2.5h,写篇题解纪念下逝去的中午 后排膜拜 1 年前就独立切掉此题的 ymx,我在 2021 年的第 5270 个小 ...

  7. [CF1063F]String Journey[后缀数组+线段树]

    题意 在 \(S\) 中找出 \(t\) 个子串满足 \(t_{i+1}\) 是 \(t_{i}\) 的子串,要让 \(t\) 最大. \(|S| \leq 5\times 10^5\). 分析 定义 ...

  8. BZOJ 2865 字符串识别(后缀数组+线段树)

    很容易想到只考虑后缀长度必须为\(max(height[rk[i]],height[rk[i]+1])+1\)(即\([i,i+x-1]\)代表的串只出现过一次)然后我正着做一遍反着做一遍,再取一个\ ...

  9. BZOJ.1396.识别子串(后缀自动机/后缀数组 线段树)

    题目链接 SAM:能成为识别子串的只有那些|right|=1的节点代表的串. 设这个节点对应原串的右端点为r[i],则如果|right[i]|=1,即\(s[\ [r_i-len_i+1,r_i-le ...

随机推荐

  1. logback日志的基本使用

    logback的日志使用,有两种方式,可以在application.yml文件中配置,不过最常见的还是用一个单独的xml配置文件进行配置: 一.application.yml配置方式 logging: ...

  2. Java与Go语言差异1 传值还是传引用

    在Java中,复杂类型(除原始类型外的其它类)作为入参,在方法中被修改后,跳出方法对象内的值仍会保持,也就是传的是引用.原始类型传的是值,如int, double等原始类型. Java代码: publ ...

  3. 标准化建筑的 FRESH 原则

    前记 大家好,我是小镭. 我在钢结构领域从业十余年,虽然工作内容是关于建筑的,但如果问我什么是建筑,我却一时答不出来. 记得小时候我读过一篇文章,说建筑是凝固的音乐. 后来我看了些书,觉得建筑是空间. ...

  4. BZOJ #2506. calc [根号分治,莫队,二分]

    \(p\) 是个正常范围, \(\sqrt p <= 100\) 比较小,预处理出来 \(a_i % p == k\) 的位置,然后丢进去,最后询问的 \(p\) 如果大于 \(100\) 就莫 ...

  5. 【计算语言学实验】基于 Skip-Gram with Negative Sampling (SGNS) 的汉语词向量学习和评估

    一.概述 训练语料来源:维基媒体 https://dumps.wikimedia.org/backup-index.html 汉语数据 用word2vec训练词向量,并用所学得的词向量,计算 pku_ ...

  6. 【笔记】机器学习 - 李宏毅 - 10 - Tips for Training DNN

    神经网络的表现 在Training Set上表现不好 ----> 可能陷入局部最优 在Testing Set上表现不好 -----> Overfitting 过拟合 虽然在机器学习中,很容 ...

  7. Python_2_daythree2_CyclicStructure

    """Function_1: for-in循环用for循环实现1~100之间的奇数求和 Time: 2020.1.27Author: YaoXie"" ...

  8. Pikachu-SSRF(服务器端请求伪造)

    SSRF(Server-Side Request Forgery:服务器端请求伪造) 其形成的原因大都是由于服务端提供了从其他服务器应用获取数据的功能,但又没有对目标地址做严格过滤与限制 导致攻击者可 ...

  9. 【UWP】在 UWP 中使用 Exceptionless 进行遥测

    2020年1月17日更新: nightly build 版本已发布 https://www.myget.org/feed/exceptionless/package/nuget/Exceptionle ...

  10. JavaSE学习笔记(5)---内部类和String类

    JavaSE学习笔记(5)---内部类和String类 一.内部类基础 转自菜鸟教程 ​ 在 Java 中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类.广泛意义上的内部类一般来 ...